From 0e5891eab7f53697b764b7e9da5163db0351a0a2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Feb 2021 19:25:48 -0500 Subject: overhaul of link anchors on text boxes to use actual Documents to represent selected text. Also got rid of _scrollY and _scrollPreviewY so that all document regions can be focused on using focus() and the new scrollFocus() mechanisim --- src/fields/documentSchemas.ts | 7 ++----- src/fields/util.ts | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'src/fields') diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index b10c2b015..f35c85110 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -26,10 +26,7 @@ export const documentSchema = createSchema({ y: "number", // y coordinate when in a freeform view z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview zIndex: "number", // zIndex of a document in a freeform view - _scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - _scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - _scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) - _scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web) + _scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) // appearance properties on the layout "_backgroundGrid-spacing": "number", // the size of the grid for collection views @@ -117,7 +114,7 @@ export const collectionSchema = createSchema({ childLayoutTemplate: Doc, // layout template to use to render children of a collecion childLayoutString: "string", //layout string to use to render children of a collection childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) - dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. + dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToAnchorID: "string", // id of anchor being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToAnchorID should be set to undefined by this doc after it sets up its scroll,etc. onChildClick: ScriptField, // script to run for each child when its clicked onChildDoubleClick: ScriptField, // script to run for each child when its clicked onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view diff --git a/src/fields/util.ts b/src/fields/util.ts index ecb3fb343..b9c5a13c1 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -96,7 +96,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - !receiver[UpdatingFromServer] && UndoManager.AddEvent({ + (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue }); -- cgit v1.2.3-70-g09d2 From 0f03183b9a2374ed3198d2b9ec8348fa819b11b4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 9 Feb 2021 03:03:48 -0500 Subject: fixed drawing link lines between everything except textanchor - to - textanchor. --- src/client/documents/Documents.ts | 2 +- src/client/util/DocumentManager.ts | 75 ++++++++++------------ .../CollectionFreeFormLinkView.tsx | 45 ++++++------- .../CollectionFreeFormLinksView.tsx | 29 ++++----- src/client/views/nodes/DocumentView.tsx | 9 +-- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/fields/Doc.ts | 7 +- 7 files changed, 79 insertions(+), 90 deletions(-) (limited to 'src/fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7d6db06d5..de4a8f43c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -789,7 +789,7 @@ export namespace Docs { } export function TextanchorDocument(options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.TEXTANCHOR), document, { targetDropAction: "move", ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.TEXTANCHOR), undefined, { targetDropAction: "move", ...options }); } export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index f5f6b6f67..2a48f74b2 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,14 +1,13 @@ import { action, observable, runInAction } from 'mobx'; import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; -import { Cast, NumCast } from '../../fields/Types'; +import { Cast, NumCast, StrCast } from '../../fields/Types'; import { returnFalse } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView } from '../views/nodes/DocumentView'; -import { LinkManager } from './LinkManager'; import { Scripting } from './Scripting'; export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; @@ -16,32 +15,28 @@ export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: ( export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) - @observable - public DocumentViews: DocumentView[] = []; - @observable LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = []; + @observable public DocumentViews: DocumentView[] = []; + @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = []; - // singleton instance private static _instance: DocumentManager; - - // create one and only one instance of NodeManager - public static get Instance(): DocumentManager { - return this._instance || (this._instance = new this()); - } + public static get Instance(): DocumentManager { return this._instance || (this._instance = new this()); } //private constructor so no other class can create a nodemanager - private constructor() { - } + private constructor() { } @action public AddView = (view: DocumentView) => { - const linksList = DocListCast(view.props.Document.links); - linksList.forEach(link => { - const linkToDoc = link && LinkManager.getOppositeAnchor(link, view.props.Document); - linkToDoc && DocumentManager.Instance.DocumentViews.filter(dv => Doc.AreProtosEqual(dv.props.Document, linkToDoc)).forEach(dv => { - if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== view.props.LayoutTemplateString) { - this.LinkedDocumentViews.push({ a: dv, b: view, l: link }); - } - }); + DocListCast(view.rootDoc.links).forEach(link => { + const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2"; + const otherDoc = link && (link[whichOtherAnchor] as Doc); + const otherDocAnno = otherDoc?.type === DocumentType.TEXTANCHOR ? otherDoc.annotationOn as Doc : undefined; + otherDoc && DocumentManager.Instance.DocumentViews. + filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)). + forEach(otherView => { + if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) { + this.LinkedDocumentViews.push({ a: whichOtherAnchor === "anchor1" ? otherView : view, b: whichOtherAnchor === "anchor1" ? view : otherView, l: link }); + } + }); }); this.DocumentViews.push(view); } @@ -56,13 +51,13 @@ export class DocumentManager { public getDocumentViewsById(id: string) { const toReturn: DocumentView[] = []; DocumentManager.Instance.DocumentViews.map(view => { - if (view.props.Document[Id] === id) { + if (view.rootDoc[Id] === id) { toReturn.push(view); } }); if (toReturn.length === 0) { DocumentManager.Instance.DocumentViews.map(view => { - const doc = view.props.Document.proto; + const doc = view.rootDoc.proto; if (doc && doc[Id] && doc[Id] === id) { toReturn.push(view); } @@ -82,14 +77,14 @@ export class DocumentManager { for (const pass of passes) { DocumentManager.Instance.DocumentViews.map(view => { - if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { + if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; return; } }); if (!toReturn) { DocumentManager.Instance.DocumentViews.map(view => { - const doc = view.props.Document.proto; + const doc = view.rootDoc.proto; if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; } @@ -107,7 +102,7 @@ export class DocumentManager { } public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { - const views = this.getDocumentViews(toFind).filter(view => view.props.Document !== originatingDoc); + const views = this.getDocumentViews(toFind).filter(view => view.rootDoc !== originatingDoc); return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); } public getDocumentViews(toFind: Doc): DocumentView[] { @@ -117,11 +112,11 @@ export class DocumentManager { // heuristic to return the "best" documents first: // choose an exact match over an alias match // choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth) - docViews.map(view => view.docViewPath.includes(LightboxView.LightboxDocView.current!) && view.props.Document === toFind && toReturn.push(view)); - docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view)); - docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view)); - docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); - docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); + docViews.map(view => view.docViewPath.includes(LightboxView.LightboxDocView.current!) && view.rootDoc === toFind && toReturn.push(view)); + docViews.map(view => view.props.PanelWidth() > 1 && view.rootDoc === toFind && toReturn.push(view)); + docViews.map(view => view.props.PanelWidth() <= 1 && view.rootDoc === toFind && toReturn.push(view)); + docViews.map(view => view.props.PanelWidth() > 1 && view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view)); + docViews.map(view => view.props.PanelWidth() <= 1 && view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view)); return toReturn; } @@ -151,7 +146,7 @@ export class DocumentManager { if (finalDocView) { const parent = targetDoc?.annotationOn as Doc; if (parent) finalDocView.layoutDoc.scrollToAnchorID = targetDoc?.[Id]; - Doc.linkFollowHighlight(finalDocView.props.Document); + Doc.linkFollowHighlight(finalDocView.rootDoc); } }; const docView = getFirstDocView(targetDoc, originatingDoc); @@ -159,16 +154,16 @@ export class DocumentManager { if (annotatedDoc && annotatedDoc !== originatingDoc?.context && !targetDoc?.isPushpin) { const first = getFirstDocView(annotatedDoc); if (first) { - annotatedDoc = first.props.Document; + annotatedDoc = first.rootDoc; first.focus(targetDoc, false); } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? const sameContext = annotatedDoc && annotatedDoc === originatingDoc?.context; if (originatingDoc?.isPushpin) { - docView.props.focus(docView.props.Document, willZoom, undefined, (didFocus: boolean) => { - if (!didFocus || docView.props.Document.hidden) { - docView.props.Document.hidden = !docView.props.Document.hidden; + docView.props.focus(docView.rootDoc, willZoom, undefined, (didFocus: boolean) => { + if (!didFocus || docView.rootDoc.hidden) { + docView.rootDoc.hidden = !docView.rootDoc.hidden; } return focusAndFinish(); }, sameContext, false);// don't want to focus the container if the source and target are in the same container, so pass 'sameContext' for dontCenter parameter @@ -176,9 +171,9 @@ export class DocumentManager { } else { docView.select(false); - docView.props.Document.hidden && (docView.props.Document.hidden = undefined); + docView.rootDoc.hidden && (docView.rootDoc.hidden = undefined); // @ts-ignore - docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext, false); + docView.props.focus(docView.rootDoc, willZoom, undefined, focusAndFinish, sameContext, false); } highlight(); } else { @@ -193,7 +188,7 @@ export class DocumentManager { const targetDocContextView = getFirstDocView(targetDocContext); if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext._viewTransition = "transform 500ms"; - targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); + targetDocContextView.props.focus(targetDocContextView.rootDoc, willZoom); // now find the target document within the context if (targetDoc._timecodeToShow) { // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode; @@ -207,7 +202,7 @@ export class DocumentManager { highlight(); } else if (delay > 1500) { // we didn't find the target, so it must have moved out of the context. Go back to just creating it. - if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document); + if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc); if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document Doc.SetInPlace(targetDoc, "annotationOn", undefined, false); createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index ae5688b48..51cb9387a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -41,44 +41,41 @@ export class CollectionFreeFormLinkView extends React.Component this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. if (!linkDoc.linkAutoMove) return; - const acont = A.props.Document.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; - const bcont = B.props.Document.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; - const adiv = (acont.length ? acont[0] : A.ContentDiv); - const bdiv = (bcont.length ? bcont[0] : B.ContentDiv); + const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; + const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; + const adiv = acont.length ? acont[0] : A.ContentDiv; + const bdiv = bcont.length ? bcont[0] : B.ContentDiv; const a = adiv.getBoundingClientRect(); const b = bdiv.getBoundingClientRect(); const { left: aleft, top: atop, width: awidth, height: aheight } = adiv.parentElement!.getBoundingClientRect(); const { left: bleft, top: btop, width: bwidth, height: bheight } = bdiv.parentElement!.getBoundingClientRect(); const apt = Utils.closestPtBetweenRectangles(aleft, atop, awidth, aheight, bleft, btop, bwidth, bheight, a.left + a.width / 2, a.top + a.height / 2); const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y); - const afield = A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1"; - const bfield = afield === "anchor1" ? "anchor2" : "anchor1"; // really hacky stuff to make the LinkAnchorBox display where we want it to: - // if there's an element in the DOM with a classname containing the link's id and a data-targetids attribute containing the other end of the link, + // if there's an element in the DOM with a classname containing a link anchor's id, // then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right // otherwise, we just use the computed nearest point on the document boundary to the target Document - const linkEles = Array.from(window.document.getElementsByClassName(linkDoc[Id])); - const targetAhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[afield] as Doc)[Id])); - const targetBhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[bfield] as Doc)[Id])); + const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement(); + const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor2 as Doc)[Id])).lastElement(); if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return; - if (!targetBhyperlink) { - A.rootDoc[afield + "_x"] = (apt.point.x - aleft) / awidth * 100; - A.rootDoc[afield + "_y"] = (apt.point.y - atop) / aheight * 100; + if (!targetAhyperlink) { + linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100; + linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100; } else { - const m = targetBhyperlink.getBoundingClientRect(); + const m = targetAhyperlink.getBoundingClientRect(); const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); - A.rootDoc[afield + "_x"] = Math.min(1, mp[0] / A.props.PanelWidth()) * 100; - A.rootDoc[afield + "_y"] = Math.min(1, mp[1] / A.props.PanelHeight()) * 100; + linkDoc.anchor1_x = Math.min(1, mp[0] / A.props.PanelWidth()) * 100; + linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100; } - if (!targetAhyperlink) { - B.rootDoc[bfield + "_x"] = (bpt.point.x - bleft) / bwidth * 100; - B.rootDoc[bfield + "_y"] = (bpt.point.y - btop) / bheight * 100; + if (!targetBhyperlink) { + linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100; + linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100; } else { - const m = targetAhyperlink.getBoundingClientRect(); + const m = targetBhyperlink.getBoundingClientRect(); const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); - B.rootDoc[bfield + "_x"] = Math.min(1, mp[0] / B.props.PanelWidth()) * 100; - B.rootDoc[bfield + "_y"] = Math.min(1, mp[1] / B.props.PanelHeight()) * 100; + linkDoc.anchor2_x = Math.min(1, mp[0] / B.props.PanelWidth()) * 100; + linkDoc.anchor2_y = Math.min(1, mp[1] / B.props.PanelHeight()) * 100; } } @@ -162,8 +159,8 @@ export class CollectionFreeFormLinkView extends React.Component { - if (!drawnPairs.reduce((found, drawnPair) => { - const match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b); - const match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a); - const match = match1 || match2; - if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { - drawnPair.l.push(connection.l); - } - return match || found; - }, false)) { - drawnPairs.push({ a: connection.a, b: connection.b, l: [connection.l] }); - } - return drawnPairs; - }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]); - return connections.filter(c => c.a.props.Document.type === DocumentType.LINK) - .map(c => ); + const connections = DocumentManager.Instance.LinkedDocumentViews + .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK) + .reduce((drawnPairs, connection) => { + const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b); + matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l)); + if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set([connection.l]) }); + return drawnPairs; + }, [] as { a: DocumentView, b: DocumentView, l: Set }[]); + const set = new Map(); + connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) })); + return Array.from(set.values()).map(c => ); } render() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 463e59bd1..bfdacdb45 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -759,18 +759,19 @@ export class DocumentViewInternal extends DocComponent !d.hidden); - return filtered.map((d, i) => + // need to use allLinks for RTF since embedded linked text anchors are not rendered with DocumentViews. All other documents render their anchors with nested DocumentViews so we just need to render the directLinks here + const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden); + return filtered.map((link, i) =>
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)} />
); } diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index d86dfd7b1..ca6c71ea3 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -134,7 +134,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent} ); - return
LinkDocPreview.LinkInfo = undefined)} onPointerEnter={action(e => LinkDocPreview.LinkInfo = { docprops: this.props, diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e1ab1d3d3..56b2db48e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -958,9 +958,10 @@ export namespace Doc { return doc; } - - export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; } - export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; } + export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { + return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) || + Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? "1" : "2"; + } export function linkFollowUnhighlight() { Doc.UnhighlightAll(); -- cgit v1.2.3-70-g09d2 From c37675d530f77ad2c077d0155d2ee3e35c55978b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 10 Feb 2021 18:31:40 -0500 Subject: fixed creating videoBox anchor to reference the whole doc when the timecode is 0. fixed following links to a document that has a colleciton context to open the context properly. --- src/client/util/DocumentManager.ts | 12 ++++-------- src/client/util/LinkManager.ts | 12 ++++++------ src/client/views/nodes/VideoBox.tsx | 3 ++- src/fields/documentSchemas.ts | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 811d04eb3..d7335c8c6 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -149,14 +149,10 @@ export class DocumentManager { const focusAndFinish = () => { finished?.(); return false; }; const highlight = () => { const finalDocView = getFirstDocView(targetDoc); - if (finalDocView) { - const parent = targetDoc?.annotationOn as Doc; - if (parent) finalDocView.layoutDoc.scrollToAnchorID = targetDoc?.[Id]; - Doc.linkFollowHighlight(finalDocView.rootDoc); - } + finalDocView && Doc.linkFollowHighlight(finalDocView.rootDoc); }; const docView = getFirstDocView(targetDoc, originatingDoc); - let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); + let annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); if (annotatedDoc && annotatedDoc !== originatingDoc?.context && !targetDoc?.isPushpin) { const first = getFirstDocView(annotatedDoc); if (first) { @@ -184,8 +180,8 @@ export class DocumentManager { highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; - const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; - const targetDocContext = annotatedDoc || contextDoc; + const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; + const targetDocContext = contextDoc || annotatedDoc; if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index a684a202d..7ad37f059 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -102,7 +102,7 @@ export class LinkManager { // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place // depending on the followLinkLocation property of the source (or the link itself as a fallback); - public static FollowLink = async (linkDoc: Opt, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => { + public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => { const batch = UndoManager.StartBatch("follow link click"); // open up target if it's not already in view ... const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => { @@ -126,9 +126,9 @@ export class LinkManager { docViewProps.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus); } }; - await LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined); + LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined); } - public static async traverseLink(link: Opt, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { + public static traverseLink(link: Opt, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : DocListCast(doc.links); 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 @@ -150,9 +150,9 @@ export class LinkManager { runInAction(() => LightboxView.LightboxDoc = (target.annotationOn as Doc) ?? target); finished?.(); } else { - const containerDoc = (await Cast(target.annotationOn, Doc)) || target; - containerDoc._currentTimecode = targetTimecode; - const targetContext = await target?.context as Doc; + const containerDoc = Cast(target.annotationOn, Doc, null) || target; + targetTimecode !== undefined && (containerDoc._currentTimecode = targetTimecode); + const targetContext = Cast(containerDoc?.context, Doc, null); const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index a821eb7c2..c0247c226 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -70,7 +70,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey + "-timeline", "videoStart", "videoEnd", Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc; + const timecode = Cast(this.layoutDoc._currentTimecode, "number", null); + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey + "-timeline", "videoStart", "videoEnd", timecode ? timecode : undefined) || this.rootDoc; } choosePath(url: string) { diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index f35c85110..056243953 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -114,7 +114,7 @@ export const collectionSchema = createSchema({ childLayoutTemplate: Doc, // layout template to use to render children of a collecion childLayoutString: "string", //layout string to use to render children of a collection childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) - dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToAnchorID: "string", // id of anchor being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToAnchorID should be set to undefined by this doc after it sets up its scroll,etc. + dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links onChildClick: ScriptField, // script to run for each child when its clicked onChildDoubleClick: ScriptField, // script to run for each child when its clicked onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view -- cgit v1.2.3-70-g09d2