aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/WebBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/WebBox.tsx')
-rw-r--r--src/client/views/nodes/WebBox.tsx414
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