diff options
Diffstat (limited to 'src/client/views/nodes/WebBox.tsx')
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 414 |
1 files changed, 167 insertions, 247 deletions
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6127f82e3..f15a249da 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,10 +1,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, 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,29 +12,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, getWordAtPoint, OmitKeys, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; -import { DragManager } from "../../util/DragManager"; -import { ImageUtils } from "../../util/Import & Export/ImageUtils"; +import { DocumentType } from '../../documents/DocumentTypes'; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { SnappingManager } from "../../util/SnappingManager"; 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 { LightboxView } from "../LightboxView"; 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"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -46,24 +45,22 @@ 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; + @observable _scrollTimer: any; + @observable private _overlayAnnoInfo: Opt<Doc>; 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; } - get webpage() { return this._iframe?.contentDocument?.children[0]; } + @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); + @observable private _scrollHeight = 1500; + @computed get scrollHeight() { return this._scrollHeight; } + @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } constructor(props: any) { super(props); @@ -71,14 +68,80 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850); } - this._annotationKey = this._annotationKey + "-" + this.urlHash(this._url); + if (this.layoutDoc[this.fieldKey + "-contentWidth"] === undefined) { + this.layoutDoc[this.fieldKey + "-contentWidth"] = Doc.NativeWidth(this.layoutDoc); + } + this._annotationKey = "annotations-" + this.urlHash(this._url); + } + + @action + createTextAnnotation = (sel: Selection, selRange: Range) => { + if (this._mainCont.current) { + const clientRects = selRange.getClientRects(); + for (let i = 0; i < clientRects.length; i++) { + const rect = clientRects.item(i); + if (rect && rect.width !== this._mainCont.current.clientWidth) { + const annoBox = document.createElement("div"); + annoBox.className = "marqueeAnnotator-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString(); + annoBox.style.left = (rect.left).toString(); + annoBox.style.width = (rect.width).toString(); + annoBox.style.height = (rect.height).toString(); + this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1); + } + } + } + //this._selectionText = selRange.cloneContents().textContent || ""; + + // clear selection + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } } + @action + iframeUp = (e: PointerEvent) => { + if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) { + this._iframe.contentDocument.addEventListener("pointerup", this.iframeUp); + const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!); + const scale = (this.props.scaling?.() || 1) * mainContBounds.scale; + const sel = this._iframe.contentWindow.getSelection(); + if (sel) { + this.createTextAnnotation(sel, sel.getRangeAt(0)); + AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, + e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale); + } + } else AnchorMenu.Instance.fadeOut(true); + } + @action + iframeDown = (e: PointerEvent) => { + const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!); + const scale = (this.props.scaling?.() || 1) * mainContBounds.scale; + const word = getWordAtPoint(e.target, e.clientX, e.clientY); + this._marqueeing = [e.clientX * scale + mainContBounds.translateX, + e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale]; + if (word) { + this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp); + 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.props.select(false); + e.stopPropagation(); + e.preventDefault(); + } + } + + @action iframeLoaded = (e: any) => { const iframe = this._iframe; if (iframe?.contentDocument) { - if (this._initialScroll !== undefined && this._outerRef.current && this.webpage) { - this.webpage.scrollTop = this._initialScroll; + iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown); + this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight); + setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); + if (this._initialScroll !== undefined && this._outerRef.current) { this._outerRef.current.scrollTop = this._initialScroll; this._initialScroll = undefined; } @@ -90,76 +153,57 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } if (href) { this.submitURL(href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin)); - if (this.webpage) { - this.webpage.scrollTop = NumCast(this.layoutDoc._scrollTop); - this.webpage.scrollLeft = 0; + if (this._outerRef.current) { + this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop); + this._outerRef.current.scrollLeft = 0; } } }))); iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false); - iframe.contentDocument.addEventListener('scroll', this.iframeScroll, false); + //iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false)); + iframe.contentDocument.addEventListener('scroll', () => { + console.log("Scroll = " + this._iframe?.scrollTop) + } + , true); } } - resetIgnoreScroll = () => { + @action + setDashScrollTop = (scrollTop: number, timeout: number = 250) => { + const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight()); + timeout = scrollTop > iframeHeight ? 0 : timeout; this._scrollTimer && clearTimeout(this._scrollTimer); - this._scrollTimer = setTimeout(() => { + this._scrollTimer = setTimeout(action(() => { this._scrollTimer = undefined; - this._ignoreScroll = ""; - }, 250); - this._outerRef.current && (this._outerRef.current.scrollLeft = 0); + if (!LinkDocPreview.LinkInfo && this._outerRef.current && + (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) { + this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop; + } + }), timeout); } + @action 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; + if (!this._scrollTimer) { + this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly } } - onScroll = (e: any) => { - if (!this._ignoreScroll.includes("iframe") && this.webpage) { - this.webpage.scrollTop = this._outerRef.current?.scrollTop || 0; - this.layoutDoc._scrollTop = this._outerRef.current?.scrollTop; - } + onWheel = (e: any) => { + e.stopPropagation(); + e.preventDefault(); } + onScroll = (e: any) => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0); scrollFocus = (doc: Doc, smooth: boolean) => { - let focusSpeed: Opt<number>; - if (doc !== this.rootDoc && this.webpage && this._outerRef.current) { + if (doc !== this.rootDoc && this._outerRef.current) { 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) { + const focusSpeed = smooth ? 500 : 0; 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); + this.goTo(scrollTo, focusSpeed); + return focusSpeed; } - } else { - this._initialScroll = NumCast(doc.y); } - - return focusSpeed; + this._initialScroll = NumCast(doc.y); + return 0; } getAnchor = () => { @@ -182,12 +226,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this._disposers.selection = reaction(() => this.props.isSelected(), selected => !selected && setTimeout(() => { - this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); this._savedAnnotations.clear(); })); - document.addEventListener("pointerup", this.onLongPressUp); - document.addEventListener("pointermove", this.onLongPressMove); const field = Cast(this.rootDoc[this.props.fieldKey], WebField); if (field?.url.href.indexOf("youtube") !== -1) { const youtubeaspect = 400 / 315; @@ -226,29 +268,22 @@ 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) { + const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight()); + scrollTop = scrollTop > iframeHeight + 50 ? iframeHeight : scrollTop; 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.setDashScrollTop(scrollTop, duration); } else { - this.webpage.scrollTop = scrollTop; - this._outerRef.current.scrollTop = scrollTop; + this.setDashScrollTop(scrollTop); } } } componentWillUnmount() { Object.values(this._disposers).forEach(disposer => disposer?.()); - document.removeEventListener("pointerup", this.onLongPressUp); - document.removeEventListener("pointermove", this.onLongPressMove); - this._iframe?.removeEventListener('wheel', this.iframeWheel); - this._iframe?.removeEventListener('scroll', this.iframeScroll); + this._iframe?.removeEventListener('wheel', this.iframeWheel, true); } @action @@ -258,7 +293,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (future.length) { history.push(this._url); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!)); - this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); + this._annotationKey = "annotations-" + this.urlHash(this._url); return true; } return false; @@ -272,7 +307,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]); else future.push(this._url); this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!)); - this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); + this._annotationKey = "annotations-" + this.urlHash(this._url); return true; } return false; @@ -299,7 +334,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum future && (future.length = 0); } this._url = newUrl; - this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url); + this._annotationKey = "annotations-" + this.urlHash(this._url); this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); } catch (e) { console.log("WebBox URL error:" + this._url); @@ -353,114 +388,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum ); } - editToggleBtn() { - return <Tooltip title={<div className="dash-tooltip" >{`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}</div>}> - <div className="webBox-annotationToggle" - style={{ color: this.props.Document.isAnnotating ? "black" : "white", backgroundColor: this.props.Document.isAnnotating ? "white" : "black" }} - onClick={action(() => this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating)}> - <FontAwesomeIcon icon="edit" size="sm" /> - </div> - </Tooltip>; - } - - _ignore = 0; - onPreWheel = (e: React.WheelEvent) => this._ignore = e.timeStamp; - onPrePointer = (e: React.PointerEvent) => this._ignore = e.timeStamp; - onPostPointer = (e: React.PointerEvent) => this._ignore !== e.timeStamp && e.stopPropagation(); - onPostWheel = (e: React.WheelEvent) => this._ignore !== e.timeStamp && e.stopPropagation(); - - onLongPressDown = (e: React.PointerEvent) => { - this._pressX = e.clientX; - this._pressY = e.clientY; - - // find the pressed element in the iframe (currently only works if its an img) - let pressedElement: HTMLElement | undefined; - let pressedBound: ClientRect | undefined; - let selectedText: string = ""; - let pressedImg: boolean = false; - if (this._iframe) { - const B = this._iframe.getBoundingClientRect(); - const iframeDoc = this._iframe.contentDocument; - if (B && iframeDoc) { - // TODO: this only works when scale = 1 as it is currently only inteded for mobile upload - const element = iframeDoc.elementFromPoint(this._pressX - B.left, this._pressY - B.top); - if (element && element.nodeName === "IMG") { - pressedBound = element.getBoundingClientRect(); - pressedElement = element.cloneNode(true) as HTMLElement; - pressedImg = true; - } else { - // check if there is selected text - const text = iframeDoc.getSelection(); - if (text && text.toString().length > 0) { - selectedText = text.toString(); - - // get html of the selected text - const range = text.getRangeAt(0); - const contents = range.cloneContents(); - const div = document.createElement("div"); - div.appendChild(contents); - pressedElement = div; - - pressedBound = range.getBoundingClientRect(); - } - } - } - } - - // mark the pressed element - if (pressedElement && pressedBound) { - if (this._iframeIndicatorRef.current) { - this._iframeIndicatorRef.current.style.top = pressedBound.top + "px"; - this._iframeIndicatorRef.current.style.left = pressedBound.left + "px"; - this._iframeIndicatorRef.current.style.width = pressedBound.width + "px"; - this._iframeIndicatorRef.current.style.height = pressedBound.height + "px"; - this._iframeIndicatorRef.current.classList.add("active"); - } - } - - // start dragging the pressed element if long pressed - this._longPressSecondsHack = setTimeout(() => { - if (pressedImg && pressedElement && pressedBound) { - e.stopPropagation(); - e.preventDefault(); - if (pressedElement.nodeName === "IMG") { - const src = pressedElement.getAttribute("src"); // TODO: may not always work - if (src) { - const doc = Docs.Create.ImageDocument(src); - ImageUtils.ExtractExif(doc); - - // add clone to div so that dragging ghost is placed properly - if (this._iframeDragRef.current) this._iframeDragRef.current.appendChild(pressedElement); - - const dragData = new DragManager.DocumentDragData([doc]); - DragManager.StartDocumentDrag([pressedElement], dragData, this._pressX, this._pressY, { hideSource: true }); - } - } - } else if (selectedText && pressedBound && pressedElement) { - e.stopPropagation(); - e.preventDefault(); - // create doc with the selected text's html - const doc = Docs.Create.HtmlDocument(pressedElement.innerHTML); - - // create dragging ghost with the selected text - if (this._iframeDragRef.current) this._iframeDragRef.current.appendChild(pressedElement); - - // start the drag - const dragData = new DragManager.DocumentDragData([doc]); - DragManager.StartDocumentDrag([pressedElement], dragData, this._pressX - pressedBound.top, this._pressY - pressedBound.top, { hideSource: true }); - } - }, 1500); - } - onLongPressMove = (e: PointerEvent) => { - // this._pressX = e.clientX; - // this._pressY = e.clientY; - } - onLongPressUp = (e: PointerEvent) => { - this._longPressSecondsHack && clearTimeout(this._longPressSecondsHack); - this._iframeIndicatorRef.current?.classList.remove("active"); - while (this._iframeDragRef.current?.firstChild) this._iframeDragRef.current.removeChild(this._iframeDragRef.current.firstChild); - } - specificContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; const funcs: ContextMenuProps[] = []; @@ -479,12 +406,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } else if (field instanceof WebField) { const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href; // view = <iframe className="webBox-iframe" src={url} onLoad={e => { e.currentTarget.before((e.currentTarget.contentDocument?.body || e.currentTarget.contentDocument)?.children[0]!); e.currentTarget.remove(); }} - view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded} + view = <iframe className="webBox-iframe" enable-annotation={"true"} + style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} + ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded} // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />; sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin"} />; } else { - view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />; + view = <iframe className="webBox-iframe" enable-annotation={"true"} + style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly + ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />; } return view; } @@ -509,7 +440,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } else { this.layoutDoc.nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); } - this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"])) + this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"])); } sidebarKey = () => this.fieldKey + "-sidebar"; sidebarFiltersHeight = () => 50; @@ -576,36 +507,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @computed get content() { - const frozen = !this.props.isSelected() || 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" : "")} - style={{ - width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`, - height: `${100 / scale}%`, - transform: `scale(${scale})` - }} - onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}> - {this.urlContent} - </div> - {!frozen ? (null) : - <div className="webBox-overlay" style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? undefined : "all" }} - onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}> - <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} > - <div className="indicator" ref={this._iframeIndicatorRef}></div> - <div className="dragger" ref={this._iframeDragRef}></div> - </div> - </div>} - </>); + return <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} + style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / (this.props.scaling?.() || 1)}%`, }}> + {this.urlContent} + </div>; } - @computed get allAnnotations() { return DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]); } - @computed get nonDocAnnotations() { return this.allAnnotations.filter(a => a.annotations); } + showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno); + @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } @computed get annotationLayer() { TraceMobx(); return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}> - {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - <Annotation {...this.props} showInfo={emptyFunction} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />) + {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => + <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />) } </div>; } @@ -617,9 +531,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this.props.select(false); } } - - @action - finishMarquee = () => this._marqueeing = undefined; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + @action finishMarquee = (x?: number, y?: number) => { + this._marqueeing = undefined; + 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); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); @@ -628,50 +545,54 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; const scale = this.props.scaling?.() || 1; return ( - <div className="webBox" ref={this._mainCont} > + <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.active() || SnappingManager.GetIsDragging() ? undefined : "none" }} > <div className={`webBox-container`} style={{ pointerEvents: inactiveLayer ? "none" : undefined }} - onWheel={this.onWebWheel} onContextMenu={this.specificContextMenu}> <base target="_blank" /> - {this.content} <div className={"webBox-outerContent"} ref={this._outerRef} style={{ - width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`, height: `${100 / scale}%`, transform: `scale(${scale})`, - pointerEvents: !this.layoutDoc.isAnnotating || inactiveLayer ? "none" : "all" + width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale}px)`, + height: `${100 / scale}%`, + transform: `scale(${scale})`, + pointerEvents: inactiveLayer ? "none" : undefined }} onWheel={this.onWheel} - onPointerDown={this.onMarqueeDown} onScroll={this.onScroll} + onPointerDown={this.onMarqueeDown} > <div className={"webBox-innerContent"} style={{ height: NumCast(this.scrollHeight, 50), pointerEvents: inactiveLayer ? "none" : undefined }}> + {this.content} <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} renderDepth={this.props.renderDepth + 1} CollectionView={undefined} fieldKey={this.annotationKey} isAnnotationOverlay={true} scaling={returnOne} + pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} ScreenToLocalTransform={this.scrollXf} + setPreviewCursor={this.setPreviewCursor} removeDocument={this.removeDocument} moveDocument={this.moveDocument} addDocument={this.addDocument} select={emptyFunction} active={this.active} whenActiveChanged={this.whenActiveChanged} /> + {this.annotationLayer} </div> </div> - {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : <MarqueeAnnotator rootDoc={this.rootDoc} anchorMenuClick={this.anchorMenuClick} - scrollTop={NumCast(this.rootDoc._scrollTop)} - down={this._marqueeing} scaling={this.props.scaling} + scrollTop={0} + down={this._marqueeing} scaling={returnOne} addDocument={this.addDocument} + docView={this.props.docViewPath().lastElement()} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} @@ -682,7 +603,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" /> </button> {this.sidebarOverlay} - {this.props.isSelected() ? this.editToggleBtn() : null} </div>); } }
\ No newline at end of file |