aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/DocumentManager.ts90
-rw-r--r--src/client/views/collections/CollectionView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx10
-rw-r--r--src/client/views/nodes/DocuLinkBox.tsx57
-rw-r--r--src/client/views/nodes/DocumentView.scss7
-rw-r--r--src/client/views/nodes/DocumentView.tsx98
-rw-r--r--src/client/views/nodes/FormattedTextBoxComment.tsx2
-rw-r--r--src/client/views/pdf/Annotation.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
-rw-r--r--src/new_fields/documentSchemas.ts3
-rw-r--r--src/new_fields/util.ts2
11 files changed, 154 insertions, 122 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 63d864db5..ae392a35b 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -5,11 +5,12 @@ import { List } from '../../new_fields/List';
import { Cast, NumCast, StrCast } from '../../new_fields/Types';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
-import { DocumentView } from '../views/nodes/DocumentView';
+import { DocumentView, DocFocusFunc } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
import { SelectionManager } from './SelectionManager';
import { DocumentType } from '../documents/DocumentTypes';
+import { UndoManager } from './UndoManager';
export class DocumentManager {
@@ -84,24 +85,20 @@ export class DocumentManager {
return this.getDocumentViewById(toFind[Id], preferredCollection);
}
- public getFirstDocumentView(toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined {
- const views = this.getDocumentViews(toFind);
- return views?.find(view => view.props.Document !== originatingDoc);
+ public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
+ return this.getDocumentViews(toFind)?.find(view => view.props.Document !== originatingDoc);
}
public getDocumentViews(toFind: Doc): DocumentView[] {
const toReturn: DocumentView[] = [];
+ const docViews = DocumentManager.Instance.DocumentViews;
- // heurstic to return the "best" documents first:
+ // 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)
- DocumentManager.Instance.DocumentViews.map(view =>
- view.props.Document.presBox === undefined && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
- DocumentManager.Instance.DocumentViews.map(view =>
- view.props.Document.presBox === undefined && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
- DocumentManager.Instance.DocumentViews.map(view =>
- view.props.Document.presBox === undefined && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
- DocumentManager.Instance.DocumentViews.map(view =>
- view.props.Document.presBox === undefined && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+ docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
+ docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
+ docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+ docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
return toReturn;
}
@@ -133,31 +130,47 @@ export class DocumentManager {
return pairs;
}
- public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false, originatingDoc: Opt<Doc> = undefined): Promise<void> => {
+ static addRightSplit = (doc: Doc, finished?: () => void) => {
+ CollectionDockingView.AddRightSplit(doc);
+ finished?.();
+ }
+ public jumpToDocument = async (
+ targetDoc: Doc,
+ willZoom: boolean,
+ dockFunc = DocumentManager.addRightSplit,
+ docContext?: Doc,
+ linkId?: string,
+ closeContextIfNotFound: boolean = false,
+ originatingDoc: Opt<Doc> = undefined,
+ finished?: () => void): Promise<void> => {
+ const getFirstDocView = DocumentManager.Instance.getFirstDocumentView;
+ const finishFalse: DocFocusFunc = () => { finished?.(); return false; };
const highlight = () => {
- const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
- finalDocView && (finalDocView.Document.scrollToLinkID = linkId);
- finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document);
+ const finalDocView = getFirstDocView(targetDoc);
+ if (finalDocView) {
+ finalDocView.Document.scrollToLinkID = linkId;
+ Doc.linkFollowHighlight(finalDocView.props.Document);
+ }
};
- const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc, originatingDoc);
+ const docView = getFirstDocView(targetDoc, originatingDoc);
let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
if (annotatedDoc) {
- const first = DocumentManager.Instance.getFirstDocumentView(annotatedDoc);
+ const first = getFirstDocView(annotatedDoc);
if (first) annotatedDoc = first.props.Document;
}
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?
- docView.props.focus(docView.props.Document, willZoom);
+ docView.props.focus(docView.props.Document, willZoom, undefined, finishFalse);
highlight();
} else {
const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
- const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined;
- const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc);
+ const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined;
+ const targetDocContext = annotatedDoc || contextDoc;
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
- (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)));
+ dockFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
highlight();
} else {
- const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext);
+ const targetDocContextView = getFirstDocView(targetDocContext);
targetDocContext.scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling
if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context
targetDocContext.panTransformType = "Ease";
@@ -166,32 +179,33 @@ export class DocumentManager {
// now find the target document within the context
if (targetDoc.displayTimecode) { // the target should show up once the video scrubs to the display timecode;
targetDocContext.currentTimecode = targetDoc.displayTimecode;
+ finished?.();
} else {
setTimeout(() => {
- const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc);
+ const retryDocView = getFirstDocView(targetDoc);
if (retryDocView) {
- retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context
+ retryDocView.props.focus(targetDoc, willZoom, undefined, finishFalse); // focus on the target if it now exists in the context
} else {
- if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document);
- targetDoc.layout && (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc))); // otherwise create a new view of the target
+ if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document);
+ targetDoc.layout && dockFunc(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), finished); // otherwise create a new view of the target
}
highlight();
}, 0);
}
} else { // there's no context view so we need to create one first and try again
- (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext);
+ dockFunc(targetDocContext);
setTimeout(() => {
- const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
- const finalDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext);
+ const finalDocView = getFirstDocView(targetDoc);
+ const finalDocContextView = getFirstDocView(targetDocContext);
setTimeout(() => // if not, wait a bit to see if the context can be loaded (e.g., a PDF). wait interval heurisitic tries to guess how we're animating based on what's just become visible
- this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true), finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created.
+ this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true, undefined, finished), finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created.
}, 0);
}
}
}
}
- public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) {
+ public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, followLinkLocation: string, afterFocus?: DocFocusFunc) => void, zoom = false, reverse = false, currentContext?: Doc, finished?: () => void) {
const linkDocs = link ? [link] : DocListCast(doc.links);
SelectionManager.DeselectAll();
const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc));
@@ -204,8 +218,10 @@ export class DocumentManager {
const linkFollowDocs = first.length ? [await first[0].anchor2 as Doc, await first[0].anchor1 as Doc] : second.length ? [await second[0].anchor1 as Doc, await second[0].anchor2 as Doc] : undefined;
const linkFollowDocContexts = first.length ? [await first[0].context as Doc, await first[0].context as Doc] : second.length ? [await second[0].context as Doc, await second[0].context as Doc] : [undefined, undefined];
const linkFollowTimecodes = first.length ? [NumCast(first[0].anchor2_timecode), NumCast(first[0].anchor1_timecode)] : second.length ? [NumCast(second[0].anchor1_timecode), NumCast(second[0].anchor2_timecode)] : [undefined, undefined];
+ const Jump = DocumentManager.Instance.jumpToDocument;
+ const Focus = (where: string) => (doc: Doc, finished?: () => void) => focus(doc, where, () => { finished?.(); return false; });
if (linkFollowDocs && linkDoc) {
- const maxLocation = StrCast(linkDoc.maximizeLocation, "inTab");
+ const followLinkLocation = StrCast(linkDoc.followLinkLocation, "inTab");
const targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined;
const target = linkFollowDocs[reverse ? 1 : 0];
const annotatedDoc = await Cast(target.annotationOn, Doc);
@@ -214,9 +230,11 @@ export class DocumentManager {
} else {
target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]);
}
- DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id], undefined, doc);
+ Jump(linkFollowDocs[reverse ? 1 : 0], zoom, Focus(followLinkLocation), targetContext, linkDoc[Id], undefined, doc, finished);
} else if (link) {
- DocumentManager.Instance.jumpToDocument(link, zoom, (doc: Doc) => focus(doc, "onRight"), undefined, undefined);
+ Jump(link, zoom, Focus("onRight"), undefined, undefined, undefined, undefined, finished);
+ } else {
+ finished?.();
}
}
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1501ce151..a727da267 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -398,6 +398,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
}
+ @computed get treeIgnoreFields() { return ["_facetCollection", "_docFilters"]; }
@computed get filterView() {
const facetCollection = this.props.Document;
const flyout = (
@@ -425,7 +426,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
treeViewHideTitle={true}
treeViewHideHeaderFields={true}
onCheckedClick={this.scriptField!}
- ignoreFields={["_facetCollection", "_docFilters"]}
+ ignoreFields={this.treeIgnoreFields}
annotationsKey={""}
dontRegisterView={true}
PanelWidth={this.facetWidth}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 376d217bb..d4e44b9a9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -842,9 +842,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
backgroundHalo: this.backgroundHalo,
parentActive: this.props.active,
bringToFront: this.bringToFront,
+ addDocTab: this.addDocTab,
};
}
+ addDocTab = (doc: Doc, where: string) => {
+ if (where === "inPlace") {
+ this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
+ return true;
+ }
+ return this.props.addDocTab(doc, where);
+ }
getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
const result = this.Document.arrangeScript?.script.run(params, console.log);
if (result?.success) {
@@ -1066,7 +1074,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (doc instanceof Doc) {
const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
doc.x = xx, doc.y = yy;
- this.props.addDocument && this.props.addDocument(doc);
+ this.props.addDocument?.(doc);
}
}
}
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx
index 81cf90f92..31ce58079 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/DocuLinkBox.tsx
@@ -4,7 +4,7 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { makeInterface } from "../../../new_fields/Schema";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils } from '../../../Utils';
+import { Utils, setupMoveUpEvents } from '../../../Utils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
import { DocComponent } from "../DocComponent";
@@ -16,6 +16,7 @@ import { ContextMenu } from "../ContextMenu";
import { LinkEditor } from "../linking/LinkEditor";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SelectionManager } from "../../util/SelectionManager";
+import { TraceMobx } from "../../../new_fields/util";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -29,8 +30,6 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
_doubleTap = false;
_lastTap: number = 0;
_ref = React.createRef<HTMLDivElement>();
- _downX = 0;
- _downY = 0;
_isOpen = false;
_timeout: NodeJS.Timeout | undefined;
@observable _x = 0;
@@ -40,56 +39,42 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
@observable _forceOpen = false;
onPointerDown = (e: React.PointerEvent) => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointerup", this.onPointerUp);
- (e.button === 0 && !e.ctrlKey) && e.stopPropagation();
+ setupMoveUpEvents(this, e, this.onPointerMove, () => { }, this.onClick);
}
- onPointerMove = action((e: PointerEvent) => {
+ onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
- if (!this._isOpen && cdiv && (Math.abs(e.clientX - this._downX) > 5 || Math.abs(e.clientY - this._downY) > 5)) {
+ if (!this._isOpen && cdiv) {
const bounds = cdiv.getBoundingClientRect();
const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
- const dragdist = Math.sqrt((pt[0] - this._downX) * (pt[0] - this._downX) + (pt[1] - this._downY) * (pt[1] - this._downY));
+ const dragdist = Math.sqrt((pt[0] - down[0]) * (pt[0] - down[0]) + (pt[1] - down[1]) * (pt[1] - down[1]));
if (separation > 100) {
- //DragManager.StartLinkTargetsDrag(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, [this.props.Document]); // Containging collection is the document, not a collection... hack.
const dragData = new DragManager.DocumentDragData([this.props.Document]);
dragData.dropAction = "alias";
- dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y"];
- DragManager.StartDocumentDrag([this._ref.current!], dragData, this._downX, this._downY);
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isButton"];
+ DragManager.StartDocumentDrag([this._ref.current!], dragData, down[0], down[1]);
+ return true;
} else if (dragdist > separation) {
this.props.Document[this.props.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
this.props.Document[this.props.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
}
}
+ return false;
});
- onPointerUp = (e: PointerEvent) => {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- if (Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && (e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) {
+ @action
+ onClick = (e: PointerEvent) => {
+ this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0);
+ this._lastTap = Date.now();
+ if ((e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) {
this.props.select(false);
}
- this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
- this._lastTap = Date.now();
- }
-
- @action
- onClick = (e: React.MouseEvent) => {
if (!this._doubleTap) {
+ const anchorContainerDoc = this.props.ContainingCollectionDoc; // bcz: hack! need a better prop for passing the anchor's container
this._editing = true;
- this.props.ContainingCollectionDoc && this.props.bringToFront(this.props.ContainingCollectionDoc, false);
- const {clientX, clientY} = e;
- if (!this.props.Document.onClick && !this._isOpen) {
+ anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
+ if (anchorContainerDoc && !this.props.Document.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- if (Math.abs(clientX - this._downX) < 3 && Math.abs(clientY - this._downY) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) {
- DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false);
- }
+ DocumentManager.Instance.FollowLink(this.props.Document, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
@@ -97,7 +82,6 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
this._timeout && clearTimeout(this._timeout);
this._timeout = undefined;
}
- e.stopPropagation();
}
openLinkDocOnRight = (e: React.MouseEvent) => {
@@ -126,6 +110,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
}
render() {
+ TraceMobx();
const x = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_x"], 100) : 0;
const y = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_y"], 100) : 0;
const c = StrCast(this.props.Document.backgroundColor, "lightblue");
@@ -143,7 +128,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
</div>
);
const small = this.props.PanelWidth() <= 1;
- return <div className={`docuLinkBox-cont${small ? "-small" : ""}`} onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
+ return <div className={`docuLinkBox-cont${small ? "-small" : ""}`} onPointerDown={this.onPointerDown} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c,
left: !small ? `calc(${x}% - 7.5px)` : undefined,
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index d1d96f0a1..ce7bcd206 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -34,13 +34,18 @@
overflow-y: scroll;
height: calc(100% - 20px);
}
-
+ .documentView-docuLinkAnchor {
+ display:flex;
+ overflow: hidden;
+ }
.documentView-docuLinkWrapper {
pointer-events: none;
position: absolute;
transform-origin: top left;
width: 100%;
height: 100%;
+ top:0;
+ left:0;
z-index: 1;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 11bf5a6a7..f74255933 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -52,6 +52,7 @@ library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsA
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone);
+export type DocFocusFunc = () => boolean;
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
@@ -74,7 +75,7 @@ export interface DocumentViewProps {
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
- focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => void;
+ focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
@@ -287,15 +288,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
Doc.UnBrushDoc(this.props.Document);
} else if (this.onClickHandler?.script) {
SelectionManager.DeselectAll();
- UndoManager.RunInBatch(() => this.onClickHandler!.script.run({
+ const func = () => this.onClickHandler!.script.run({
this: this.props.Document,
self: Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document,
containingCollection: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
- }, console.log) && !this.props.Document.isButton && this.select(false), "on click");
+ }, console.log) && !this.props.Document.isButton && this.select(false);
+ if (this.props.Document !== Doc.UserDoc().undoBtn && this.props.Document !== Doc.UserDoc().redoBtn) {
+ UndoManager.RunInBatch(func, "on click");
+ } else func();
} else if (this.Document.type === DocumentType.BUTTON) {
UndoManager.RunInBatch(() => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click");
} else if (this.Document.isButton) {
- UndoManager.RunInBatch(() => this.buttonClick(e.altKey, e.ctrlKey), "on link button follow");
+ DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey);
} else {
if (this.props.Document.isTemplateForField && !(e.ctrlKey || e.button > 0)) {
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
@@ -309,14 +313,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
- const linkDocs = DocListCast(this.props.Document.links);
- if (linkDocs.length) {
- DocumentManager.Instance.FollowLink(undefined, this.props.Document,
- // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
- (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, maxLocation)),
- ctrlKey, altKey, this.props.ContainingCollectionDoc);
- }
+ // 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);
+ followLinkClick = async (altKey: boolean, ctrlKey: boolean) => {
+ const batch = UndoManager.StartBatch("follow link click");
+ const docFollowLoc = StrCast(this.Document.followLinkLocation);
+ const { focus, addDocTab } = this.props;
+ await DocumentManager.Instance.FollowLink(undefined, this.props.Document,
+ // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards (except if 'inPlace', then leave alone)
+ (doc, followLoc, afterFocus) => focus(this.props.Document, true, 1, () => {
+ addDocTab(doc, docFollowLoc || followLoc) && focus(doc, true, undefined, afterFocus);
+ return (docFollowLoc || followLoc) === "inPlace" ? false : true;
+ }),
+ ctrlKey, altKey, this.props.ContainingCollectionDoc, batch.end);
}
handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
@@ -564,6 +574,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.Document.onClick = undefined;
} else {
this.Document.isButton = true;
+ this.Document.followLinkLocation = undefined;
+ }
+ }
+
+ @undoBatch
+ toggleFollowInPlace = (): void => {
+ if (this.Document.isButton) {
+ this.Document.isButton = false;
+ } else {
+ this.Document.isButton = true;
+ this.Document.followLinkLocation = "inPlace";
}
}
@@ -693,6 +714,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+ onClicks.push({ description: this.Document.isButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleButtonBehavior, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
!existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
@@ -981,31 +1003,36 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return this._link;
}
+ hideLinkAnchor = (doc: Doc) => undoBatch(doc => doc.hidden = true)();
+ anchorPanelWidth = () => this.props.PanelWidth() || 1;
+ anchorPanelHeight = () => this.props.PanelHeight() || 1;
+ @computed get anchors() {
+ TraceMobx();
+ return DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+ <div className="documentView-docuLinkWrapper" key={d[Id]}>
+ <DocumentView {...this.props}
+ Document={d}
+ ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the DocuLinkBox
+ PanelWidth={this.anchorPanelWidth}
+ PanelHeight={this.anchorPanelHeight}
+ layoutKey={this.linkEndpoint(d)}
+ ContentScaling={returnOne}
+ backgroundColor={returnTransparent}
+ removeDocument={this.hideLinkAnchor} />
+ </div>);
+ }
@computed get innards() {
TraceMobx();
- if (!this.props.PanelWidth()) {
- return <div style={{ display: "flex", overflow: "hidden" }}>
+ if (!this.props.PanelWidth()) { // this happens when the document is a tree view label
+ return <div className="documentView-docuLinkAnchor" >
{StrCast(this.props.Document.title)}
- {this.Document.links && DocListCast(this.Document.links).filter(d => !d.hidden).filter(this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-docuLinkWrapper" style={{ position: "absolute", top: 0, left: 0 }} key={`${d[Id]}`}>
- <DocumentView {...this.props}
- Document={d}
- ContainingCollectionDoc={this.props.Document}
- PanelWidth={returnOne} PanelHeight={returnOne}
- layoutKey={this.linkEndpoint(d)} ContentScaling={returnOne}
- backgroundColor={returnTransparent}
- removeDocument={undoBatch(doc => doc.hidden = true)} />
- </div>)}
+ {this.anchors}
</div>;
}
const showTitle = StrCast(this.layoutDoc._showTitle);
const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
const showCaption = StrCast(this.layoutDoc._showCaption);
const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
- const searchHighlight = (!this.Document.searchFields ? (null) :
- <div className="documentView-searchHighlight">
- {this.Document.searchFields}
- </div>);
const captionView = (!showCaption ? (null) :
<div className="documentView-captionWrapper">
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
@@ -1032,26 +1059,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
/>
</div>);
return <>
- {this.Document.links && DocListCast(this.Document.links).filter(d => !d.hidden).filter(this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-docuLinkWrapper" key={`${d[Id]}`}>
- <DocumentView {...this.props} ContentScaling={returnOne} ContainingCollectionDoc={this.props.Document} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => doc.hidden = true)} />
- </div>)}
+ {this.anchors}
{!showTitle && !showCaption ?
- this.Document.searchFields ?
- (<div className="documentView-searchWrapper">
- {this.contents}
- {searchHighlight}
- </div>)
- :
- this.contents
- :
+ this.contents :
<div className="documentView-styleWrapper" >
<div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
{this.contents}
</div>
{titleView}
{captionView}
- {searchHighlight}
</div>
}
</>;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index d1a563494..babca2a14 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -84,7 +84,7 @@ export class FormattedTextBoxComment {
const textBox = FormattedTextBoxComment.textBox;
if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
- (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"));
+ (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"));
} else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d23c81065..5cb766c03 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -98,7 +98,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
const annoGroup = await Cast(this.props.document.group, Doc);
if (annoGroup) {
DocumentManager.Instance.FollowLink(undefined, annoGroup,
- (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"),
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"),
false, false, undefined);
e.stopPropagation();
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c032f019d..25ceb6218 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -585,7 +585,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
dragComplete: e => {
if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
- if (link) link.maximizeLocation = "onRight";
+ if (link) link.followLinkLocation = "onRight";
}
}
});
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 3ac46362a..1a5a471a0 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -46,12 +46,11 @@ export const documentSchema = createSchema({
treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
currentTimecode: "number", // current play back time of a temporal document (video / audio)
- maximizeLocation: "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., onRight, inPlace, inTab, )
lockedPosition: "boolean", // whether the document can be moved (dragged)
lockedTransform: "boolean", // whether the document can be panned/zoomed
inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently
borderRounding: "string", // border radius rounding of document
- searchFields: "string", // the search fields to display when this document matches a search in its metadata
heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
isButton: "boolean", // whether document functions as a button (overiding native interactions of its content)
ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events)
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 8c719ccd8..480a55da0 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -12,7 +12,7 @@ function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
-const tracing = false;
+const tracing = true;
export function TraceMobx() {
tracing && trace();
}