From ded69655dabf97c76f97271e7da8e77e3f33ec25 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 15 Mar 2021 03:36:35 -0400 Subject: getting webBox's to work like PdfBox's - text selection & marquee. --- src/client/views/nodes/WebBox.tsx | 158 +++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 27 deletions(-) (limited to 'src/client/views/nodes/WebBox.tsx') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6127f82e3..9e6e94248 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -37,6 +37,7 @@ 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]>; @@ -64,6 +65,7 @@ export class WebBox extends ViewBoxAnnotatableComponent = new Dictionary(); get scrollHeight() { return this.webpage?.scrollHeight || 1000; } get webpage() { return this._iframe?.contentDocument?.children[0]; } + @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } constructor(props: any) { super(props); @@ -71,12 +73,114 @@ export class WebBox extends ViewBoxAnnotatableComponent { + 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 + ptrUp = (e: PointerEvent) => { + if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) { + this._iframe.contentDocument.addEventListener("pointerup", this.ptrUp); + 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); + } + + getWordAtPoint = (elem: any, x: number, y: number): Opt => { + if (elem.nodeType == elem.TEXT_NODE) { + var range = elem.ownerDocument.createRange(); + range.selectNodeContents(elem); + var currentPos = 0; + var endPos = range.endOffset; + while (currentPos + 1 < endPos) { + range.setStart(elem, currentPos); + range.setEnd(elem, currentPos + 1); + const rangeRect = range.getBoundingClientRect(); + if (rangeRect.left <= x && rangeRect.right >= x && + rangeRect.top <= y && rangeRect.bottom >= y) { + range.expand("word"); + var ret = range.toString(); + range.detach(); + return (ret); + } + currentPos += 1; + } + } else { + for (var i = 0; i < elem.childNodes.length; i++) { + var range = elem.childNodes[i].ownerDocument.createRange(); + range.selectNodeContents(elem.childNodes[i]); + const rangeRect = range.getBoundingClientRect(); + if (rangeRect.left <= x && rangeRect.right >= x && + rangeRect.top <= y && rangeRect.bottom >= y) { + range.detach(); + const word = this.getWordAtPoint(elem.childNodes[i], x, y); + if (word) return word; + } else { + range.detach(); + } + } + } + return undefined; + } + @action + ptrDown = (e: PointerEvent) => { + const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!); + const scale = (this.props.scaling?.() || 1) * mainContBounds.scale; + const word = this.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) { + 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.props.select(false); + e.stopPropagation(); + e.preventDefault(); + } } iframeLoaded = (e: any) => { 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; this._outerRef.current.scrollTop = this._initialScroll; @@ -134,6 +238,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { 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; } } @@ -258,7 +364,7 @@ export class WebBox extends ViewBoxAnnotatableComponent([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 +405,7 @@ export class WebBox extends ViewBoxAnnotatableComponent{`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}}> -
this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating)}> - -
- ; - } - _ignore = 0; onPreWheel = (e: React.WheelEvent) => this._ignore = e.timeStamp; onPrePointer = (e: React.PointerEvent) => this._ignore = e.timeStamp; @@ -583,7 +679,6 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.urlContent} @@ -599,13 +694,16 @@ export class WebBox extends ViewBoxAnnotatableComponent); } - @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) => this._overlayAnnoInfo = anno); + @observable private _overlayAnnoInfo: Opt; + @computed get allAnnotations() { + return DocListCast(this.dataDoc[this.annotationKey]); + } @computed get annotationLayer() { TraceMobx(); return
- {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - ) + {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => + ) }
; } @@ -617,9 +715,13 @@ export class WebBox extends ViewBoxAnnotatableComponent this._marqueeing = undefined; + finishMarquee = () => { + this._marqueeing = undefined; + this.isAnnotating = 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); @@ -634,11 +736,12 @@ export class WebBox extends ViewBoxAnnotatableComponent - {this.content}
+ {this.content} + {this.annotationLayer}
- {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : {this.sidebarOverlay} - {this.props.isSelected() ? this.editToggleBtn() : null} ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2