From aed4a386bf57ba7b1b144bacd39f9f9ccabe0dfd Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 10 Feb 2021 22:32:29 -0500 Subject: simplified focus'ing on documents. refactored scrollFocus code. changed focus in 2D to move doc into view but not center. --- src/Utils.ts | 7 +++ src/client/util/DocumentManager.ts | 11 ++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 44 ++++++------------- src/client/views/nodes/DocumentView.tsx | 18 ++++---- src/client/views/nodes/PDFBox.tsx | 3 +- src/client/views/nodes/WebBox.tsx | 50 ++++++++++++---------- .../views/nodes/formattedText/FormattedTextBox.tsx | 10 ++++- src/client/views/pdf/PDFViewer.tsx | 15 ++++--- 8 files changed, 80 insertions(+), 78 deletions(-) (limited to 'src') 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 { + 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 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 { 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 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, props: Opt, 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 { - 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 { 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 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 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 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 { + 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 -- cgit v1.2.3-70-g09d2