aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2021-02-10 22:32:29 -0500
committerbobzel <zzzman@gmail.com>2021-02-10 22:32:29 -0500
commitaed4a386bf57ba7b1b144bacd39f9f9ccabe0dfd (patch)
tree537ea7b01c98e5cdde362984292c4db3173e98c4
parentf240c85ff0adee914b43d9d169fa31260e03265d (diff)
simplified focus'ing on documents. refactored scrollFocus code. changed focus in 2D to move doc into view but not center.
-rw-r--r--src/Utils.ts7
-rw-r--r--src/client/util/DocumentManager.ts11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx44
-rw-r--r--src/client/views/nodes/DocumentView.tsx18
-rw-r--r--src/client/views/nodes/PDFBox.tsx3
-rw-r--r--src/client/views/nodes/WebBox.tsx50
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx10
-rw-r--r--src/client/views/pdf/PDFViewer.tsx15
8 files changed, 80 insertions, 78 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index c7074c3da..061c74611 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -191,6 +191,13 @@ export namespace Utils {
return { h: h, s: s, l: l };
}
+ export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number) {
+ if (scrollTop + contextHgt < targetY + Math.max(targetHgt * 1.1, 100)) {
+ return Math.min(scrollTop, targetY + Math.max(targetHgt * 1.1, 100) - contextHgt);
+ } else if (scrollTop > targetY - targetHgt * .1) {
+ return Math.max(0, targetY - targetHgt * .1);
+ }
+ }
export function clamp(n: number, lower: number, upper: number) {
return Math.max(lower, Math.min(upper, n));
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 9a8b662e7..ac7710d34 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -150,29 +150,26 @@ export class DocumentManager {
};
const docView = getFirstDocView(targetDoc, originatingDoc);
let annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
- if (annotatedDoc && annotatedDoc !== originatingDoc?.context && !targetDoc?.isPushpin) {
+ if (!docView && annotatedDoc && annotatedDoc !== originatingDoc?.context && targetDoc.type === DocumentType.TEXTANCHOR) {
const first = getFirstDocView(annotatedDoc);
if (first) {
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;
+ } else 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?
if (originatingDoc?.isPushpin) {
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
- //finished?.();
+ });
}
else {
docView.select(false);
docView.rootDoc.hidden && (docView.rootDoc.hidden = undefined);
// @ts-ignore
- docView.props.focus(docView.rootDoc, willZoom, undefined, focusAndFinish, sameContext, false);
+ docView.props.focus(docView.rootDoc, willZoom, undefined, focusAndFinish);
}
highlight();
} else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a52522def..68a65dfe7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -885,7 +885,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
}
- focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
+ focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc) => {
const state = HistoryUtil.getState();
// TODO This technically isn't correct if type !== "doc", as
@@ -903,31 +903,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
SelectionManager.DeselectAll();
if (this.props.Document.scrollHeight) {
- // only consider the document to be an annotation if it's an annotation on this collection's document (ignore annotations on some other document that are somehow being focused on here)
- const annotOn = Doc.AreProtosEqual(doc.annotationOn as Doc, this.props.Document) ? Cast(doc.annotationOn, Doc) as Doc : undefined;
- let delay = 1000;
- if (!annotOn) {
- !dontCenter && this.props.focus(doc);
- afterFocus && setTimeout(afterFocus, delay);
- } else {
- const contextHgt = this.props.PanelHeight();
- const curScroll = NumCast(this.props.Document._scrollTop);
- let scrollTo = curScroll;
- if (curScroll + contextHgt < NumCast(doc.y)) {
- scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 100) - contextHgt;
- } else if (curScroll > NumCast(doc.y)) {
- scrollTo = Math.max(0, NumCast(doc.y) - 50);
- }
- if (curScroll !== scrollTo || this.props.Document._viewTransition) {
- delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0;
- !dontCenter && this.props.focus(this.props.Document);
- afterFocus && setTimeout(() => afterFocus?.(delay ? true : false), delay);
- } else {
- !dontCenter && delay && this.props.focus(this.props.Document);
- afterFocus?.(!dontCenter && delay ? true : false);
- }
- }
-
+ this.props.focus(doc, undefined, undefined, afterFocus);
} else {
const layoutdoc = Doc.Layout(doc);
const savedState = { px: NumCast(this.Document._panX), py: NumCast(this.Document._panY), s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
@@ -937,8 +913,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
let newPanY = savedState.py;
if (!layoutdoc.annotationOn) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
willZoom && this.setScaleToZoom(layoutdoc, scale);
- newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeWidth(this.props.Document)) / 2 / this.zoomScaling() : 0);
- newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeHeight(this.props.Document)) / 2 / this.zoomScaling() : 0);
+ const cx = NumCast(this.props.Document._panX);
+ const cy = NumCast(this.props.Document._panY);
+ const pwid = this.props.PanelWidth() / NumCast(this.props.Document._viewScale, 1);
+ const phgt = this.props.PanelHeight() / NumCast(this.props.Document._viewScale, 1);
+ const screen = { left: cx - pwid / 2, right: cx + pwid / 2, top: cy - phgt / 2, bot: cy + phgt / 2 };
+ const bounds = { left: NumCast(doc.x) - pwid / 10, right: NumCast(doc.x) + doc[WidthSym]() + pwid / 10, top: NumCast(doc.y) - phgt / 10, bot: NumCast(doc.y) + doc[HeightSym]() + phgt / 10 };
+ newPanX = cx + Math.min(0, bounds.left - screen.left) + Math.max(0, bounds.right - screen.right);
+ newPanY = cy + Math.min(0, bounds.top - screen.top) + Math.max(0, bounds.bot - screen.bot);
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
}
@@ -949,12 +931,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
Doc.BrushDoc(this.props.Document);
- const newDidFocus = didFocus || (newPanX !== savedState.px || newPanY !== savedState.py);
-
const newAfterFocus = (didFocus: boolean) => {
afterFocus && setTimeout(() => {
// @ts-ignore
- if (afterFocus?.(!dontCenter && (didFocus || (newPanX !== savedState.px || newPanY !== savedState.py)))) {
+ if (afterFocus?.(didFocus || (newPanX !== savedState.px || newPanY !== savedState.py))) {
this.Document._panX = savedState.px;
this.Document._panY = savedState.py;
this.Document[this.scaleFieldKey] = savedState.s;
@@ -964,7 +944,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}, newPanX !== savedState.px || newPanY !== savedState.py ? 500 : 0);
return false;
};
- this.props.focus(this.props.Document, undefined, undefined, newAfterFocus, undefined, newDidFocus);
+ this.props.focus(this.props.Document, undefined, undefined, newAfterFocus);
!doc.hidden && Doc.linkFollowHighlight(doc);
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 47668d0f3..a5698e138 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, HeightSym } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -44,11 +44,11 @@ import { LinkDocPreview } from "./LinkDocPreview";
import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
-export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
+export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc) => void;
export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => any;
export interface DocComponentView {
getAnchor: () => Doc;
- scrollFocus?: (doc: Doc, smooth: boolean) => void;
+ scrollFocus?: (doc: Doc, smooth: boolean, afterFocus?: DocAfterFocusFunc) => void;
back?: () => boolean;
forward?: () => boolean;
url?: () => string;
@@ -376,9 +376,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
}
- focus = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => {
- this._componentView?.scrollFocus?.(doc, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
- return this.props.focus(doc, willZoom, scale, afterFocus, dontCenter, focused);
+ focus = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc) => {
+ if (this._componentView?.scrollFocus) {
+ return this._componentView?.scrollFocus?.(doc, !LinkDocPreview.LinkInfo, afterFocus); // bcz: smooth parameter should really be passed into focus() instead of inferred here
+ }
+ return this.props.focus(doc, willZoom, scale, afterFocus);
}
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
@@ -917,8 +919,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
contentsActive = () => this.docView?.contentsActive();
- focus = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => {
- return this.docView?.focus(doc, willZoom, scale, afterFocus, dontCenter, focused);
+ focus = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, focused?: boolean) => {
+ return this.docView?.focus(doc, willZoom, scale, afterFocus, focused);
}
getBounds = () => {
if (!this.docView || !this.docView.ContentDiv || this.docView.props.renderDepth === 0 || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 496caedaa..4b926bb6f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -22,6 +22,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
+import { DocAfterFocusFunc } from './DocumentView';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -79,7 +80,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
}
}
- scrollFocus = (doc: Doc, smooth: boolean) => doc !== this.rootDoc && this._pdfViewer?.scrollFocus(doc, smooth);
+ scrollFocus = (doc: Doc, smooth: boolean, afterFocus?: DocAfterFocusFunc) => { this._pdfViewer?.scrollFocus(doc, smooth, afterFocus); }
getAnchor = () => this.rootDoc;
componentWillUnmount() { this._selectReactionDisposer?.(); }
componentDidMount() {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ee152ddb3..d3d58b2f8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -29,6 +29,7 @@ import { Annotation } from "../pdf/Annotation";
import { FieldView, FieldViewProps } from './FieldView';
import "./WebBox.scss";
import React = require("react");
+import { DocFocusFunc, DocAfterFocusFunc, DocumentView } from "./DocumentView";
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -110,22 +111,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
getAnchor = () => this.rootDoc;
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, smooth: boolean, afterFocus?: DocAfterFocusFunc) => {
if (doc !== this.rootDoc && this.webpage && this._outerRef.current) {
- this._initialScroll !== undefined && (this._initialScroll = NumCast(doc.y));
- this._ignoreScroll = true;
- if (smooth) {
- smoothScroll(500, this.webpage as any as HTMLElement, NumCast(doc.y));
- smoothScroll(500, this._outerRef.current, NumCast(doc.y));
- } else {
- this.webpage.scrollTop = NumCast(doc.y);
- this._outerRef.current.scrollTop = NumCast(doc.y);
+ const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+ if (scrollTo !== undefined) {
+ this._initialScroll !== undefined && (this._initialScroll = scrollTo);
+ this._ignoreScroll = true;
+ this.goTo(scrollTo, smooth ? 500 : 0);
+ this.layoutDoc._scrollTop = scrollTo;
+ this._ignoreScroll = false;
+ return afterFocus?.(true);
}
- smooth && (this.layoutDoc._scrollTop = NumCast(doc.y));
- this._ignoreScroll = false;
} else {
this._initialScroll = NumCast(doc.y);
}
+ afterFocus?.(false);
}
async componentDidMount() {
@@ -164,29 +164,35 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
}
- var quickScroll: string | undefined = "";
+ var quickScroll = true;
this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
(scrollTop) => {
- if (quickScroll !== undefined) {
+ if (quickScroll) {
this._initialScroll = scrollTop;
}
- else if (!this._ignoreScroll && this._outerRef.current && this.webpage) {
+ else if (!this._ignoreScroll) {
const viewTrans = StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
- if (duration) {
- smoothScroll(duration, this.webpage as any as HTMLElement, scrollTop);
- smoothScroll(duration, this._outerRef.current, scrollTop);
- } else {
- this.webpage.scrollTop = scrollTop;
- this._outerRef.current.scrollTop = scrollTop;
- }
+ this.goTo(scrollTop, duration);
}
},
{ fireImmediately: true }
);
- quickScroll = undefined;
+ quickScroll = false;
+ }
+
+ goTo = (scrollTop: number, duration: number) => {
+ if (this._outerRef.current && this.webpage) {
+ if (duration) {
+ smoothScroll(duration, this.webpage as any as HTMLElement, scrollTop);
+ smoothScroll(duration, this._outerRef.current, scrollTop);
+ } else {
+ this.webpage.scrollTop = scrollTop;
+ this._outerRef.current.scrollTop = scrollTop;
+ }
+ }
}
componentWillUnmount() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c15c30803..183719e31 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -67,6 +67,7 @@ import { AnchorMenu } from '../../pdf/AnchorMenu';
import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
import { DocumentManager } from '../../../util/DocumentManager';
import { LightboxView } from '../../LightboxView';
+import { DocAfterFocusFunc } from '../DocumentView';
const translateGoogleApi = require("translate-google-api");
export interface FormattedTextBoxProps {
@@ -867,7 +868,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
return this.active();//this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
}
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, smooth: boolean, afterFocus?: DocAfterFocusFunc) => {
const anchorId = doc[Id];
const findAnchorFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
@@ -905,8 +906,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
const escAnchorId = anchorId[0] > '0' && anchorId[0] <= '9' ? `\\3${anchorId[0]} ${anchorId.substr(1)}` : anchorId;
addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: "yellow" });
- setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), 1500);
+ setTimeout(() => {
+ clearStyleSheetRules(FormattedTextBox._highlightStyleSheet);
+ afterFocus?.(true);
+ }, 1500);
}
+ } else {
+ afterFocus?.(false);
}
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0477192d5..dd9dfa733 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -28,6 +28,7 @@ import { AnchorMenu } from "./AnchorMenu";
import "./PDFViewer.scss";
const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import React = require("react");
+import { DocAfterFocusFunc } from "../nodes/DocumentView";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
const _global = (window /* browser */ || global /* node */) as any;
@@ -179,15 +180,17 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
// scrolls to focus on a nested annotation document. if this is part a link preview then it will jump to the scroll location,
// otherwise it will scroll smoothly.
- scrollFocus = (doc: Doc, smooth: boolean) => {
+ scrollFocus = (doc: Doc, smooth: boolean, afterFocus?: DocAfterFocusFunc) => {
const mainCont = this._mainCont.current;
- if (mainCont) {
- if (smooth) {
- smoothScroll(500, mainCont, NumCast(doc.y));
- } else {
- mainCont.scrollTop = NumCast(doc.y);
+ if (doc !== this.rootDoc && mainCont) {
+ const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+ if (scrollTo !== undefined) {
+ if (smooth) smoothScroll(500, mainCont, scrollTo);
+ else mainCont.scrollTop = scrollTo;
+ return afterFocus?.(true);
}
}
+ afterFocus?.(false);
}
@action