diff options
author | bobzel <zzzman@gmail.com> | 2021-03-15 13:19:24 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-03-15 13:19:24 -0400 |
commit | 363b73726523caff1e5f047287c7fdb242b39e20 (patch) | |
tree | 7680e88640c34edd06a882f911b89d71f4e02ce4 /src | |
parent | ded69655dabf97c76f97271e7da8e77e3f33ec25 (diff) |
fixed scrolling to annotations and pointerevents on webBox (when an annotation was selected so you can drag out a marquee)
Diffstat (limited to 'src')
-rw-r--r-- | src/Utils.ts | 13 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 5 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 122 |
4 files changed, 41 insertions, 101 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index f22df0da2..936a459ba 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -482,7 +482,7 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start; }; -export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, finish?: () => void, reset?: { resetGoTo: { to: number, duration: number } | undefined }) { +export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number) { const elements = (element instanceof HTMLElement ? [element] : element); let starts = elements.map(element => element.scrollTop); let startDate = new Date().getTime(); @@ -490,23 +490,12 @@ export function smoothScroll(duration: number, element: HTMLElement | HTMLElemen const animateScroll = () => { const currentDate = new Date().getTime(); let currentTime = currentDate - startDate; - const resetParams = reset?.resetGoTo; - if (resetParams) { - reset!.resetGoTo = undefined; - const { to: newTo, duration: newDuration } = resetParams; - to = newTo; - starts = starts.map(start => easeInOutQuad(currentTime, start, to - start, duration)); - startDate = currentDate; - duration = newDuration; - currentTime = currentDate - startDate; - } elements.map((element, i) => element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration)); if (currentTime < duration) { requestAnimationFrame(animateScroll); } else { elements.forEach(element => element.scrollTop = to); - finish?.(); } }; animateScroll(); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index c43dd6ba8..59500070c 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -30,7 +30,7 @@ export interface MarqueeAnnotatorProps { annotationLayer: HTMLDivElement; addDocument: (doc: Doc) => boolean; getPageFromScroll?: (top: number) => number; - finishMarquee: () => void; + finishMarquee: (x?: number, y?: number) => void; anchorMenuClick?: (anchor: Doc) => void; } @observer @@ -204,10 +204,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { if (AnchorMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up this.highlight("rgba(245, 230, 95, 0.75)", false); // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color) } + this.props.finishMarquee(); } else { runInAction(() => this._width = this._height = 0); + this.props.finishMarquee(e.clientX, e.clientY); } - this.props.finishMarquee(); } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index daa92055c..b5dca42a7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1493,7 +1493,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P onDragOver={e => e.preventDefault()} onContextMenu={this.onContextMenu} style={{ - pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents, + pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents as any, transform: `scale(${this.contentScaling || 1})`, width: `${100 / (this.contentScaling || 1)}%`, height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 9e6e94248..0f10d7cae 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,10 +1,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Dictionary } from "typescript-collections"; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { HtmlField } from "../../../fields/HtmlField"; @@ -14,30 +13,30 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, OmitKeys, returnOne, smoothScroll, Utils, returnZero, returnTrue } from "../../../Utils"; +import { emptyFunction, OmitKeys, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; +import { DocumentType } from '../../documents/DocumentTypes'; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { undoBatch } from "../../util/UndoManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionStackingView } from "../collections/CollectionStackingView"; +import { CollectionViewType } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { MarqueeAnnotator } from "../MarqueeAnnotator"; +import { AnchorMenu } from "../pdf/AnchorMenu"; import { Annotation } from "../pdf/Annotation"; +import { SearchBox } from "../search/SearchBox"; +import { StyleProp } from "../StyleProvider"; import { FieldView, FieldViewProps } from './FieldView'; +import { FormattedTextBox } from "./formattedText/FormattedTextBox"; import { LinkDocPreview } from "./LinkDocPreview"; import "./WebBox.scss"; -import { DocumentType } from '../../documents/DocumentTypes'; import React = require("react"); -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { SearchBox } from "../search/SearchBox"; -import { CollectionStackingView } from "../collections/CollectionStackingView"; -import { StyleProp } from "../StyleProvider"; -import { FormattedTextBox } from "./formattedText/FormattedTextBox"; -import { CollectionViewType } from "../collections/CollectionView"; -import { AnchorMenu } from "../pdf/AnchorMenu"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -47,20 +46,21 @@ const WebDocument = makeInterface(documentSchema); export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + private _outerRef: React.RefObject<HTMLDivElement> = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _longPressSecondsHack?: NodeJS.Timeout; - private _outerRef = React.createRef<HTMLDivElement>(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _iframeIndicatorRef = React.createRef<HTMLDivElement>(); private _iframeDragRef = React.createRef<HTMLDivElement>(); private _keyInput = React.createRef<HTMLInputElement>(); - private _ignoreScroll = ""; private _scrollTimer: any; private _initialScroll: Opt<number>; + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @observable private _marqueeing: number[] | undefined; @observable private _url: string = "hello"; @observable private _pressX: number = 0; @observable private _pressY: number = 0; + @observable private _isAnnotating = false; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); get scrollHeight() { return this.webpage?.scrollHeight || 1000; } @@ -121,7 +121,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } } else AnchorMenu.Instance.fadeOut(true); } - getWordAtPoint = (elem: any, x: number, y: number): Opt<string> => { if (elem.nodeType == elem.TEXT_NODE) { var range = elem.ownerDocument.createRange(); @@ -166,11 +165,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale]; if (word) { - console.log(word); this._iframe?.contentDocument?.addEventListener("pointerup", this.ptrUp); setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. } else { - this.isAnnotating = true; + this._isAnnotating = true; this.props.select(false); e.stopPropagation(); e.preventDefault(); @@ -181,8 +179,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const iframe = this._iframe; if (iframe?.contentDocument) { iframe?.contentDocument.addEventListener("pointerdown", this.ptrDown); - if (this._initialScroll !== undefined && this._outerRef.current && this.webpage) { - this.webpage.scrollTop = this._initialScroll; + if (this._initialScroll !== undefined && this._outerRef.current) { this._outerRef.current.scrollTop = this._initialScroll; this._initialScroll = undefined; } @@ -201,65 +198,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } }))); iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false); - iframe.contentDocument.addEventListener('scroll', this.iframeScroll, false); } } - resetIgnoreScroll = () => { + resetIgnoreScroll = (timeout: number = 250) => { this._scrollTimer && clearTimeout(this._scrollTimer); this._scrollTimer = setTimeout(() => { this._scrollTimer = undefined; - this._ignoreScroll = ""; - }, 250); - this._outerRef.current && (this._outerRef.current.scrollLeft = 0); - } - iframeWheel = (e: any) => { - this._ignoreScroll = "iframe"; - this.resetIgnoreScroll(); - e.stopPropagation(); - } - onWebWheel = (e: React.WheelEvent) => { - this._ignoreScroll = "iframe"; - this.goTo(Math.max(0, (this.webpage?.scrollTop || 0) + (this._accumulatedGoTo + 1) * e.deltaY), 100); - this.resetIgnoreScroll(); - e.stopPropagation(); - } - onWheel = (e: React.WheelEvent) => { - this._ignoreScroll = "outer"; - this.resetIgnoreScroll(); - e.stopPropagation(); - } - iframeScroll = (e: any) => { - if (!this._ignoreScroll.includes("outer") && this._outerRef.current) { - this._outerRef.current.scrollTop = this.webpage?.scrollTop || 0; - this.layoutDoc._scrollTop = this.webpage?.scrollTop; - } - } - onScroll = (e: any) => { - if (!this._ignoreScroll.includes("iframe") && this.webpage) { - this.webpage.scrollTop = this._outerRef.current?.scrollTop || 0; - } - if (this._ignoreScroll !== "iframe|outer") { - this.layoutDoc._scrollTop = this._outerRef.current?.scrollTop; - } + if (!LinkDocPreview.LinkInfo && this._outerRef.current) { + this.layoutDoc._scrollTop = this._outerRef.current.scrollTop; + } + }, timeout); } + iframeWheel = (e: any) => e.stopPropagation(); + onScroll = (e: any) => this.resetIgnoreScroll(); scrollFocus = (doc: Doc, smooth: boolean) => { let focusSpeed: Opt<number>; - if (doc !== this.rootDoc && this.webpage && this._outerRef.current) { + if (doc !== this.rootDoc && this.webpage) { const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : 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); - if (!LinkDocPreview.LinkInfo) { - this._ignoreScroll = "iframe|outer"; - this.layoutDoc._scrollTop = scrollTo; - this._ignoreScroll = ""; - } - this._ignoreScroll = "iframe|outer"; this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0); - setTimeout(() => { - this._scrollTimer = undefined; - this._ignoreScroll = ""; - }, focusSpeed); } } else { this._initialScroll = NumCast(doc.y); @@ -332,19 +291,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum quickScroll = false; } - _accumulatedGoTo = 0; - _resetGoTo: { resetGoTo: { to: number, duration: number } | undefined } = { resetGoTo: undefined }; goTo = (scrollTop: number, duration: number) => { - if (this._outerRef.current && this.webpage) { + if (this._outerRef.current) { if (duration) { - if (this._accumulatedGoTo++) { - this._resetGoTo.resetGoTo = { to: scrollTop, duration }; - } else { - smoothScroll(duration, [this.webpage as any as HTMLElement, this._outerRef.current], scrollTop, () => this._accumulatedGoTo = 0, this._resetGoTo); - } + smoothScroll(duration, [this._outerRef.current], scrollTop); + this.resetIgnoreScroll(duration); } else { - this.webpage.scrollTop = scrollTop; - this._outerRef.current.scrollTop = scrollTop; + this.resetIgnoreScroll(); } } } @@ -354,7 +307,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum document.removeEventListener("pointerup", this.onLongPressUp); document.removeEventListener("pointermove", this.onLongPressMove); this._iframe?.removeEventListener('wheel', this.iframeWheel); - this._iframe?.removeEventListener('scroll', this.iframeScroll); } @action @@ -672,10 +624,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @computed get content() { - const frozen = !this.props.isSelected() || DocumentDecorations.Instance?.Interacting; + const frozen = !this.active() || DocumentDecorations.Instance?.Interacting; const scale = this.props.scaling?.() || 1; return (<> - <div className={"webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} + <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`, height: `${100 / scale}%`, @@ -715,12 +667,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this.props.select(false); } } - @observable isAnnotating = false; - - @action - finishMarquee = () => { + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + @action finishMarquee = (x?: number, y?: number) => { this._marqueeing = undefined; - this.isAnnotating = false; + this._isAnnotating = false; + x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false); } panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); @@ -733,7 +684,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum <div className="webBox" ref={this._mainCont} > <div className={`webBox-container`} style={{ pointerEvents: inactiveLayer ? "none" : undefined }} - onWheel={this.onWebWheel} onContextMenu={this.specificContextMenu}> <base target="_blank" /> <div className={"webBox-outerContent"} ref={this._outerRef} @@ -743,9 +693,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum transform: `scale(${scale})`, pointerEvents: inactiveLayer ? "none" : "all" }} - onWheel={this.onWheel} - onPointerDown={this.onMarqueeDown} onScroll={this.onScroll} + onPointerDown={this.onMarqueeDown} > <div className={"webBox-innerContent"} style={{ height: NumCast(this.scrollHeight, 50), @@ -758,10 +707,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum fieldKey={this.annotationKey} isAnnotationOverlay={true} scaling={returnOne} - pointerEvents={this.isAnnotating ? "all" : "none"} + pointerEvents={this._isAnnotating ? "all" : "none"} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} ScreenToLocalTransform={this.scrollXf} + setPreviewCursor={this.setPreviewCursor} removeDocument={this.removeDocument} moveDocument={this.moveDocument} addDocument={this.addDocument} |