diff options
author | Lionel Han <47760119+IGoByJoe@users.noreply.github.com> | 2020-09-04 17:11:59 -0700 |
---|---|---|
committer | Lionel Han <47760119+IGoByJoe@users.noreply.github.com> | 2020-09-04 17:11:59 -0700 |
commit | d9deafd2f368137a01a424f898edf0dc4acd2eae (patch) | |
tree | 4b3930cfcefc6fd29daeb54c2b4f14322a470c5c | |
parent | 111b1a0fc74089bd83511ca46491b7fefc136f62 (diff) | |
parent | 2ef7900d1210bc0e5261e1d1f8fd1ba5f3a0ee4c (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into bug_fixes
21 files changed, 106 insertions, 68 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9062add8e..8d1f8a04e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -888,19 +888,21 @@ export namespace DocUtils { if (d.z) return true; for (const facetKey of Object.keys(filterFacets)) { const facet = filterFacets[facetKey]; - const satisfiesFacet = Object.keys(facet).some(value => { - if (facet[value] === "match") { - if (facetKey.startsWith("*")) { // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc - const allKeys = Array.from(Object.keys(d)); - allKeys.push(...Object.keys(Doc.GetProto(d))); - const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); - return keys.some(key => Field.toString(d[key] as Field).includes(value)); - } - return /*d[facetKey] === undefined || */Field.toString(d[facetKey] as Field).includes(value); + const matches = Object.keys(facet).filter(value => facet[value] === "match"); + const checks = Object.keys(facet).filter(value => facet[value] === "check"); + const xs = Object.keys(facet).filter(value => facet[value] === "x"); + const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); + const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); + const satisfiesMatchFacets = !matches.length ? true : matches.some(value => { + if (facetKey.startsWith("*")) { // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc + const allKeys = Array.from(Object.keys(d)); + allKeys.push(...Object.keys(Doc.GetProto(d))); + const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); + return keys.some(key => Field.toString(d[key] as Field).includes(value)); } - return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value); + return Field.toString(d[facetKey] as Field).includes(value); }); - if (!satisfiesFacet) { + if (!satisfiesCheckFacets || !satisfiesMatchFacets || failsNotEqualFacets) { return false; } } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a9f4458a1..7a1c193c1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -874,7 +874,7 @@ export class CurrentUserUtils { // Import sidebar is where shared documents are contained static setupImportSidebar(doc: Doc) { if (doc.myImportDocs === undefined) { - doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My ImportDocuments", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, lockedPosition: true, _chromeStatus: "disabled", system: true })); + doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My ImportDocuments", forceActive: true, ignoreClick: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, lockedPosition: true, _chromeStatus: "disabled", system: true })); } if (doc.myImportPanel === undefined) { const uploads = Cast(doc.myImportDocs, Doc, null); @@ -1064,7 +1064,7 @@ export class CurrentUserUtils { const disposer = OverlayView.ShowSpinner(); DocListCastAsync(importDocs.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); - list?.push(...results); + list?.splice(0, 0, ...results); disposer(); }); } else { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 2c7dcf49b..420b29559 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -215,8 +215,8 @@ export class DocumentManager { public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : DocListCast(doc.links); SelectionManager.DeselectAll(); - const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); // link docs where 'doc' is anchor1 - const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); // link docs where 'doc' is anchor2 + const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1 + const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2 const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; @@ -225,10 +225,10 @@ export class DocumentManager { followLinks.forEach(async linkDoc => { if (linkDoc) { const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 : - (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : - (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); if (target) { const containerDoc = (await Cast(target.annotationOn, Doc)) || target; containerDoc._currentTimecode = targetTimecode; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1bd2bdb9d..9797f5488 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -144,7 +144,7 @@ export namespace DragManager { linkSourceDocument: Doc; dontClearTextBox?: boolean; linkDocument?: Doc; - linkDropCallback?: (data: LinkDragData) => void; + linkDropCallback?: (data: { linkDocument?: Doc }) => void; } export class ColumnDragData { constructor(colKey: SchemaHeaderField) { @@ -161,7 +161,7 @@ export namespace DragManager { this.annotationDocument = annotationDoc; this.offset = [0, 0]; } - linkedToDoc?: boolean; + linkDocument?: Doc; targetContext: Doc | undefined; dragDocument: Doc; annotationDocument: Doc; @@ -169,6 +169,7 @@ export namespace DragManager { offset: number[]; dropAction: dropActionType; userDropAction: dropActionType; + linkDropCallback?: (data: { linkDocument?: Doc }) => void; } export function MakeDropTarget( diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 6162d044a..68e0b91b0 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -141,7 +141,7 @@ .colorFlyout { margin-top: 2px; - margin-right: 25px; + margin-right: 18px; &:hover { cursor: pointer; @@ -162,6 +162,7 @@ .preferences-color { display: flex; + margin-top: 2px; .preferences-color-text { color: black; @@ -173,6 +174,8 @@ .preferences-font { display: flex; + height: 23px; + margin-top: 2px; .preferences-font-text { color: black; @@ -193,6 +196,16 @@ } } + .preferences-check { + color: black; + font-size: 9; + /* margin-top: 4; */ + margin-right: 4; + margin-bottom: -3; + margin-left: 5; + margin-top: -1px; + } + .size-select { width: 60px; color: black; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 1794b3ace..0a1890ca4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -93,15 +93,15 @@ export class SettingsManager extends React.Component<{}> { <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} > {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)} </select> - <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}> + <select className="size-select" style={{ marginRight: "10px" }} onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}> {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)} </select> <div> - Show title + <div className="preferences-check">Show title</div> <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = !Doc.UserDoc().showTitle} checked={BoolCast(Doc.UserDoc().showTitle)} /> </div> <div> - Alt Btns + <div className="preferences-check">Alt Buttons</div> <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"]} checked={BoolCast(Doc.UserDoc()["documentLinksButton-hideEnd"])} /> </div> diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 349fd077c..952100cb0 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -215,7 +215,7 @@ export class ContextMenu extends React.Component { @computed get menuItems() { if (!this._searchString) { - return this._items.map(item => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} />); + return this._items.map(item => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} pageX={this.pageX} />); } return this.filteredViews; } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 22bfbe217..eddecb7a7 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -4,6 +4,7 @@ import { observer } from "mobx-react"; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { UndoManager } from "../util/UndoManager"; +import { NumberLiteralType } from "typescript"; export interface OriginalMenuProps { description: string; @@ -28,6 +29,7 @@ export type ContextMenuProps = OriginalMenuProps | SubmenuProps; export class ContextMenuItem extends React.Component<ContextMenuProps & { selected?: boolean }> { @observable private _items: Array<ContextMenuProps> = []; @observable private overItem = false; + @observable private subRef = React.createRef<HTMLDivElement>(); constructor(props: ContextMenuProps | SubmenuProps) { super(props); @@ -51,6 +53,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select currentTimeout?: any; static readonly timeout = 300; _overPosY = 0; + _overPosX = 0; onPointerEnter = (e: React.MouseEvent) => { if (this.currentTimeout) { clearTimeout(this.currentTimeout); @@ -60,6 +63,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select return; } this._overPosY = e.clientY; + this._overPosX = e.clientX; this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout); } @@ -75,6 +79,9 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select } render() { + + + if ("event" in this.props) { return ( <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}> @@ -91,8 +98,13 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select } else if ("subitems" in this.props) { const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center"; const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : ""; + + // here const submenu = !this.overItem ? (null) : - <div className="contextMenu-subMenu-cont" style={{ marginLeft: "90%", left: "0px", marginTop }}> + <div className="contextMenu-subMenu-cont" + style={{ + marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? "90%" : "20%", marginTop + }}> {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)} </div>; if (!("noexpand" in this.props)) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a87a07b62..f9b3b1da8 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -170,6 +170,14 @@ export class MainView extends React.Component { window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page window.addEventListener("dragover", e => e.preventDefault(), false); document.addEventListener("pointerdown", this.globalPointerDown); + document.addEventListener("click", (e: MouseEvent) => { + if (!e.cancelBubble) { + const pathstr = (e as any)?.path.map((p: any) => p.classList?.toString()).join(); + if (pathstr.includes("libraryFlyout")) { + SelectionManager.DeselectAll(); + } + } + }); } initAuthenticationRouters = async () => { diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 80f116029..e5f9e0417 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -47,7 +47,7 @@ } .propertiesView-settings { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; font-size: 12.5px; font-weight: bold; @@ -87,7 +87,7 @@ } .propertiesView-sharing { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; .propertiesView-sharing-title { @@ -150,7 +150,7 @@ } .propertiesView-appearance { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; .propertiesView-appearance-title { @@ -187,7 +187,7 @@ } .propertiesView-transform { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; .propertiesView-transform-title { @@ -322,7 +322,7 @@ } .propertiesView-fields { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; .propertiesView-fields-title { @@ -394,6 +394,7 @@ cursor: auto; } } + .propertiesView-contexts { .propertiesView-contexts-title { @@ -465,7 +466,7 @@ } .propertiesView-presTrails { - border-bottom: 1px solid black; + //border-bottom: 1px solid black; //padding: 8.5px; .propertiesView-presTrails-title { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5c83eabd1..46f38795c 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -907,14 +907,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </div> {!this.openSharing ? (null) : <div className="propertiesView-sharing-content"> - <div className="propertiesView-acls-checkbox"> - <Checkbox - color="primary" - onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} - checked={this.layoutDocAcls} - />; - <div className="propertiesView-acls-checkbox-text">Layout</div> - </div> {this.sharingTable} {/* <div className="change-buttons"> <button diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 30a71398b..306dfe797 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1167,7 +1167,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if ((e as any).handlePan || this.props.isAnnotationOverlay) return; (e as any).handlePan = true; - if (this._marqueeRef?.current) { + if (!this.props.Document._noAutoscroll && this._marqueeRef?.current) { const dragX = e.detail.clientX; const dragY = e.detail.clientY; const bounds = this._marqueeRef.current?.getBoundingClientRect(); diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 2ae87ac13..8e09052a3 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -66,7 +66,8 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { } render() { - const groupItems = this.props.group.map(linkDoc => { + const set = new Set<Doc>(this.props.group); + const groupItems = Array.from(set.keys()).map(linkDoc => { 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) { diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 1d346894c..ab6cae0ad 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -220,7 +220,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp @computed get linkButton() { TraceMobx(); - const links = this.props.links; + const links = new Set<Doc>(this.props.links); const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link"; const buttonTitle = "Tap to view links"; @@ -265,7 +265,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp <FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */} {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> : - link : links.length} + link : Array.from(links).length} </div> {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? @@ -287,7 +287,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } </div >; - return (!links.length) && !this.props.AlwaysOn ? (null) : + return (!Array.from(links).length) && !this.props.AlwaysOn ? (null) : this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ? <Tooltip title={<><div className="dash-tooltip">{title}</div></>}> {linkButton} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 41e6d9603..f6360fc87 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -661,10 +661,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (de.complete.annoDragData) { /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner e.stopPropagation(); - de.complete.annoDragData.linkedToDoc = true; - - const linkDoc = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link"); - linkDoc && makeLink(linkDoc); + de.complete.annoDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link"); + de.complete.annoDragData.linkDocument && makeLink(de.complete.annoDragData.linkDocument); } if (de.complete.linkDragData) { e.stopPropagation(); diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 7fcbce9e3..790901a29 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -135,7 +135,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc render() { const facetCollection = this.props.Document.proto as Doc; const flyout = <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}> - {this._allFacets.map(facet => <label className="filterBox-flyout-face" key={`${facet}`} onClick={e => this.facetClick(facet)}> + {this._allFacets.map(facet => <label className="filterBox-flyout-facet" key={`${facet}`} onClick={e => this.facetClick(facet)}> <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} /> <span className="checkmark" /> {facet} diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index ce8df5195..dddefc17f 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -22,6 +22,7 @@ interface Props { } @observer export class LinkDocPreview extends React.Component<Props> { + static TargetDoc: Doc | undefined; @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 = ""; @@ -42,12 +43,14 @@ export class LinkDocPreview extends React.Component<Props> { LinkDocPreview.LinkInfo = undefined; this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "add:right"), false) : null; } + componentWillUnmount() { LinkDocPreview.TargetDoc = undefined; } componentDidUpdate() { this.updatePreview(); } componentDidMount() { this.updatePreview(); } async updatePreview() { const linkDoc = this.props.linkDoc; const linkSrc = this.props.linkSrc; + LinkDocPreview.TargetDoc = undefined; 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)))); @@ -59,7 +62,7 @@ export class LinkDocPreview extends React.Component<Props> { const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor; runInAction(() => { this._toolTipText = ""; - this._targetDoc = target; + LinkDocPreview.TargetDoc = this._targetDoc = target; if (anchor !== this._targetDoc && anchor && this._targetDoc) { this._targetDoc._scrollY = NumCast(anchor?.y); } @@ -74,8 +77,8 @@ export class LinkDocPreview extends React.Component<Props> { this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right"); } } - width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)); - height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)); + width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)) - 16; + height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)) - 16 @computed get targetDocView() { return !this._targetDoc ? <div style={{ @@ -105,8 +108,8 @@ export class LinkDocPreview extends React.Component<Props> { ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} renderDepth={0} - PanelWidth={() => this.width() - 16} //Math.min(350, NumCast(target._width, 350))} - PanelHeight={() => this.height() - 16} //Math.min(250, NumCast(target._height, 250))} + PanelWidth={this.width} //Math.min(350, NumCast(target._width, 350))} + PanelHeight={this.height} //Math.min(250, NumCast(target._height, 250))} focus={emptyFunction} whenActiveChanged={returnFalse} bringToFront={returnFalse} @@ -120,7 +123,7 @@ export class LinkDocPreview extends React.Component<Props> { return <div className="linkDocPreview" style={{ position: "absolute", left: this.props.location[0], - top: this.props.location[1], width: this.width(), height: this.height(), + top: this.props.location[1], width: this.width() + 16, height: this.height() + 16, zIndex: 1000, border: "8px solid white", borderRadius: "7px", boxShadow: "3px 3px 1.5px grey", diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index f9e6227d7..c5d7c3c9f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -499,8 +499,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (annotationDoc) { DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, { dragComplete: e => { - if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) { - DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); + if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) { + e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); annotationDoc.isLinkButton = true; } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 436538eba..311143ff7 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -460,8 +460,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } else if (de.complete.linkDragData) { de.complete.linkDragData.linkDropCallback = this.linkDrop; } + else if (de.complete.annoDragData) { + de.complete.annoDragData.linkDropCallback = this.linkDrop; + } } - linkDrop = (data: DragManager.LinkDragData) => { + linkDrop = (data: { linkDocument?: Doc }) => { const linkDoc = data.linkDocument!; const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-"; const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : ""; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0a52f6ad5..18be9b679 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -32,6 +32,7 @@ import { PDFMenu } from "./PDFMenu"; import "./PDFViewer.scss"; const pdfjs = require('pdfjs-dist/es5/build/pdf.js'); import React = require("react"); +import { LinkDocPreview } from "../nodes/LinkDocPreview"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -98,6 +99,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @observable private _zoomed = 1; private _pdfViewer: any; + private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); @@ -166,7 +168,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu (scrollY) => { if (scrollY !== undefined) { (this._showCover || this._showWaiting) && this.setupPdfJsViewer(); - this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0)); + (!LinkDocPreview.TargetDoc) && this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0)); setTimeout(() => this.Document._scrollY = undefined, 1000); } }, @@ -435,11 +437,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu // if alt+left click, drag and annotate this._downX = e.clientX; this._downY = e.clientY; - addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" }); + (e.target as any).tagName === "SPAN" && (this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" })); if ((this.Document._viewScale || 1) !== 1) return; if ((e.button !== 0 || e.altKey) && this.active(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true); - //e.stopPropagation(); } this._marqueeing = false; if (!e.altKey && e.button === 0 && this.active(true)) { @@ -461,12 +462,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._marqueeHeight = this._marqueeWidth = 0; this._marqueeing = true; } - document.removeEventListener("pointermove", this.onSelectMove); document.addEventListener("pointermove", this.onSelectMove); - document.removeEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointerup", this.onSelectEnd); + document.addEventListener("pointerup", this.removeStyle, true); } } + removeStyle = () => { + clearStyleSheetRules(PDFViewer._annotationStyle); + document.removeEventListener("pointerup", this.removeStyle); + } @action onSelectMove = (e: PointerEvent): void => { @@ -605,11 +609,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu if (annotationDoc) { DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, { dragComplete: e => { - if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) { - const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); - annotationDoc.isLinkButton = true; - if (link) Doc.GetProto(link).followLinkLocation = "default"; + if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) { + e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); + annotationDoc.isLinkButton = true; // prevents link button fro showing up --- maybe not a good thing? } + e.annoDragData && e.annoDragData.linkDocument && e.annoDragData?.linkDropCallback?.({ linkDocument: e.annoDragData.linkDocument }); } }); } diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 76b26a9aa..71294c59c 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -95,7 +95,7 @@ export const documentSchema = createSchema({ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) + followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., add:right, inPlace, default, ) hideLinkButton: "boolean", // whether the blue link counter button should be hidden hideAllLinks: "boolean", // whether all individual blue anchor dots should be hidden linkDisplay: "boolean", // whether a link connection should be shown between link anchor endpoints. |