From f072a9f2c1ad69eb8ae4242fa5d2e18bbd94f6ef Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 May 2022 11:45:44 -0400 Subject: changed following links to open up icons all the way down to the target document. allow StartLink to work with webBox w/ text selection. fixed web clippings to have a cursor and grab keyDown events. --- src/client/views/nodes/PDFBox.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 69662d53a..48465976a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -75,6 +75,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + return; // currently we render pdf icons as text labels const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); -- cgit v1.2.3-70-g09d2 From f79a109aaa2c89d1462a1299c9e4bd0a0305e115 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 11 May 2022 13:32:40 -0400 Subject: added cropped images of pdfs --- src/client/views/nodes/PDFBox.tsx | 89 +++++++++++++++++++++++++++++--- src/client/views/nodes/WebBoxRenderer.js | 26 +++++----- src/client/views/pdf/PDFViewer.tsx | 8 ++- 3 files changed, 101 insertions(+), 22 deletions(-) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 48465976a..98c17ed23 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -2,28 +2,30 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; -import { CreateImage } from "../nodes/WebBoxRenderer"; import "pdfjs-dist/web/pdf_viewer.css"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; -import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types'; +import { Id } from '../../../fields/FieldSymbols'; +import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; -import { Docs } from '../../documents/Documents'; +import { Docs, DocUtils } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { Colors } from '../global/globalEnums'; +import { CreateImage } from "../nodes/WebBoxRenderer"; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PDFViewer } from "../pdf/PDFViewer"; import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; +import { ImageBox } from './ImageBox'; import "./PDFBox.scss"; -import React = require("react"); -import { Id } from '../../../fields/FieldSymbols'; import { VideoBox } from './VideoBox'; +import React = require("react"); @observer export class PDFBox extends ViewBoxAnnotatableComponent() { @@ -61,10 +63,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + if (!region) return; + const cropping = Doc.MakeCopy(region, true); + Doc.GetProto(region).lockedPosition = true; + Doc.GetProto(region).title = "region:" + this.rootDoc.title; + Doc.GetProto(region).isPushpin = true; + this.addDocument(region); + + const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; + const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; + newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); + newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); + this.replaceCanvases(docViewContent, newDiv); + const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv); + + const anchx = NumCast(cropping.x); + const anchy = NumCast(cropping.y); + const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1); + const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1); + const viewScale = 1; + cropping.title = "crop: " + this.rootDoc.title; + cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); + cropping.y = NumCast(this.rootDoc.y); + cropping._width = anchw; + cropping._height = anchh; + cropping.isLinkButton = undefined; + const croppingProto = Doc.GetProto(cropping); + croppingProto.annotationOn = undefined; + croppingProto.isPrototype = true; + croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.type = DocumentType.IMG; + croppingProto.layout = ImageBox.LayoutString("data"); + croppingProto.data = new ImageField(Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")); + croppingProto["data-nativeWidth"] = anchw; + croppingProto["data-nativeHeight"] = anchh; + if (addCrop) { + DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", ""); + } + this.props.bringToFront(cropping); + + CreateImage( + "", + document.styleSheets, + htmlString, + anchw, + anchh, + NumCast(region.y) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"]), + NumCast(region.x) * this.props.PanelWidth() / NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]), + 4 + ).then + ((data_url: any) => { + VideoBox.convertDataUri(data_url, region[Id]).then( + returnedfilename => setTimeout(action(() => { + croppingProto.data = new ImageField(returnedfilename); + }), 500)); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + + + return cropping; + } + updateIcon = () => { return; // currently we render pdf icons as text labels const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; @@ -328,7 +399,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent @@ -386,4 +458,5 @@ export class PDFBox extends ViewBoxAnnotatableComponent} */ - const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll) { + const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll, xoff) { return new Promise(async function (resolve, reject) { @@ -240,7 +240,7 @@ var ForeignHtmlRenderer = function (styleSheets) { // build SVG string const svg = ` - + ${contentRootElemString} `; @@ -259,11 +259,11 @@ var ForeignHtmlRenderer = function (styleSheets) { * * @return {Promise} */ - this.renderToImage = async function (webUrl, html, width, height, scroll) { + this.renderToImage = async function (webUrl, html, width, height, scroll, xoff) { return new Promise(async function (resolve, reject) { const img = new Image(); console.log("BUILDING SVG for:" + webUrl); - img.src = await buildSvgDataUri(webUrl, html, width, height, scroll); + img.src = await buildSvgDataUri(webUrl, html, width, height, scroll, xoff); img.onload = function () { console.log("IMAGE SVG created:" + webUrl); @@ -279,16 +279,16 @@ var ForeignHtmlRenderer = function (styleSheets) { * * @return {Promise} */ - this.renderToCanvas = async function (webUrl, html, width, height, scroll) { + this.renderToCanvas = async function (webUrl, html, width, height, scroll, xoff, oversample) { return new Promise(async function (resolve, reject) { - const img = await self.renderToImage(webUrl, html, width, height, scroll); + const img = await self.renderToImage(webUrl, html, width, height, scroll, xoff); const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; + canvas.width = img.width * oversample; + canvas.height = img.height * oversample; const canvasCtx = canvas.getContext('2d'); - canvasCtx.drawImage(img, 0, 0, img.width, img.height); + canvasCtx.drawImage(img, 0, 0, img.width * oversample, img.height * oversample); resolve(canvas); }); @@ -301,9 +301,9 @@ var ForeignHtmlRenderer = function (styleSheets) { * * @return {Promise} */ - this.renderToBase64Png = async function (webUrl, html, width, height, scroll) { + this.renderToBase64Png = async function (webUrl, html, width, height, scroll, xoff, oversample) { return new Promise(async function (resolve, reject) { - const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll); + const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll, xoff, oversample); resolve(canvas.toDataURL('image/png')); }); }; @@ -311,8 +311,8 @@ var ForeignHtmlRenderer = function (styleSheets) { }; -export function CreateImage(webUrl, styleSheets, html, width, height, scroll) { - const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/docView-hack/g, 'documentView-hack').replace(/\n/g, "").replace(//g, ""), width, height, scroll); +export function CreateImage(webUrl, styleSheets, html, width, height, scroll, xoff = 0, oversample = 1) { + const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/docView-hack/g, 'documentView-hack').replace(/\n/g, "").replace(//g, ""), width, height, scroll, xoff, oversample); return val; } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d3876906e..3c052d10b 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -48,6 +48,7 @@ interface IViewerProps extends FieldViewProps { setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); + crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined; } /** @@ -194,6 +195,9 @@ export class PDFViewer extends React.Component { } return focusSpeed; } + crop = (region: Doc | undefined, addCrop?: boolean) => { + return this.props.crop(region, addCrop); + } @action setupPdfJsViewer = async () => { @@ -589,7 +593,9 @@ export class PDFViewer extends React.Component { finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} />} + mainCont={this._mainCont.current} + anchorMenuCrop={this.crop} + />} ; } -- cgit v1.2.3-70-g09d2 From b6448779f56b3bcad108e8a2c8e75ac405524a3e Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 11 May 2022 17:08:17 -0400 Subject: fixed crop of pdfs when zoomed. removed some unused pdf code. removed unused LOD code. --- src/client/documents/Documents.ts | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +- src/client/views/nodes/PDFBox.tsx | 7 +- src/client/views/pdf/PDFViewer.scss | 50 ++++--- src/client/views/pdf/PDFViewer.tsx | 158 ++++++++++----------- src/fields/documentSchemas.ts | 1 - 6 files changed, 113 insertions(+), 107 deletions(-) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dd3e13cfd..d9c2e0d8b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -116,7 +116,6 @@ export class DocumentOptions { _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed - _freeformLOD?: boolean; // whether to use LOD to render a freeform document _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target _showTitle?: string; // field name to display in header (:hover is an optional suffix) _showCaption?: string; // which field to display in the caption area. leave empty to have no caption diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5b3a7db41..193750d8a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1558,7 +1558,6 @@ export class CollectionFreeFormView extends CollectionSubView Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); if (!Doc.UserDoc().noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); - optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" }); } !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); const mores = ContextMenu.Instance.findByDescription("More..."); @@ -1739,7 +1738,7 @@ export class CollectionFreeFormView extends CollectionSubView - {this._firstRender || (this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0) ? + {this._firstRender ? this.placeholder : this.marqueeView} {this.props.noOverlay ? (null) : } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 98c17ed23..e6e723ef1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -65,9 +65,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; const curPage = NumCast(this.Document._curPage) || 1; - return !this.props.isContentActive() ? (null) : + return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? (null) :
[KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 390aed1e0..822af6a68 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,5 +1,3 @@ - - .pdfViewer-content { height: 100%; width: 100%; @@ -8,33 +6,42 @@ top: 0; left: 0; } -.pdfViewerDash, .pdfViewerDash-interactive { + +.pdfViewerDash, +.pdfViewerDash-interactive { position: absolute; width: 100%; height: 100%; top: 0; - left:0; + left: 0; position: absolute; overflow-y: auto; overflow-x: hidden; transform-origin: top left; - + // .canvasWrapper { // transform: scale(0.75); // transform-origin: top left; // } .textLayer { opacity: unset; - mix-blend-mode: multiply;// bcz: makes text fuzzy! + mix-blend-mode: multiply; // bcz: makes text fuzzy! + span { padding-right: 5px; padding-bottom: 4px; } } - .textLayer ::selection { background: #ACCEF7; } // should match the backgroundColor in createAnnotation() + + .textLayer ::selection { + background: #ACCEF7; + } + + // should match the backgroundColor in createAnnotation() .textLayer .highlight { background-color: yellow; } + .textLayer .highlight.selected { background-color: orange; } @@ -43,32 +50,32 @@ position: relative; border: unset; } + .pdfViewerDash-text-selected { - // position: relative; // bcz: this breaks auto-scrolling using the inline search box + // position: relative; // bcz: this breaks auto-scrolling using the inline search box z-index: -1; - .textLayer{ - pointer-events: all; - user-select: text; + + .textLayer { + pointer-events: all; + user-select: text; } } + .pdfViewerDash-text { transform-origin: top left; + .textLayer { will-change: transform; } } - .pdfViewerDash-overlay, .pdfViewerDash-overlay-inking { + .pdfViewerDash-overlay { transform-origin: left top; position: absolute; top: 0px; left: 0px; display: inline-block; - width:100%; - pointer-events: all; - } - .pdfViewerDash-overlay { - pointer-events: none; + width: 100%; } .pdfViewerDash-overlayAnno { @@ -81,7 +88,7 @@ border-radius: 5px; display: block; } - + .pdfViewerDash-annotationLayer { position: absolute; transform-origin: left top; @@ -90,12 +97,13 @@ pointer-events: none; mix-blend-mode: multiply; // bcz: makes text fuzzy! } + .pdfViewerDash-waiting { width: 70%; height: 70%; - margin : 15%; + margin: 15%; transition: 0.4s opacity ease; - opacity: 0.7; + opacity: 0.7; position: absolute; z-index: 10; } @@ -103,4 +111,4 @@ .pdfViewerDash-interactive { pointer-events: all; -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 3c052d10b..38890410c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,18 +1,16 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, HeightSym, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; -import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { CompiledScript, CompileScript } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { SharingManager } from "../../util/SharingManager"; import { SnappingManager } from "../../util/SnappingManager"; @@ -59,11 +57,9 @@ export class PDFViewer extends React.Component { static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _savedAnnotations = new ObservableMap(); - @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private _marqueeing: number[] | undefined; @observable private _textSelecting = true; @observable private _showWaiting = true; - @observable private _showCover = false; @observable private _zoomed = 1; @observable private _overlayAnnoInfo: Opt; @observable private Index: number = -1; @@ -79,14 +75,13 @@ export class PDFViewer extends React.Component { private _selectionText: string = ""; private _downX: number = 0; private _downY: number = 0; - private _coverPath: any; private _lastSearch = false; private _viewerIsSetup = false; private _ignoreScroll = false; private _initialScroll: Opt; private _forcedScroll = true; - + @observable isAnnotating = false; // key where data is stored @computed get allAnnotations() { return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + "-annotations"]), this.props.docFilters(), this.props.docRangeFilters(), undefined); @@ -108,13 +103,11 @@ export class PDFViewer extends React.Component { if (pathComponents.length) { params.subtree = `${pathComponents.join("/")}/`; } - this._coverPath = href.startsWith(window.location.origin) ? await Networking.PostToServer("/thumbnail", params) : { width: 100, height: 100, path: "" }; } else { const params: any = { coreFilename: relative.split("/")[relative.split("/").length - 1], pageNum: Math.min(this.props.pdf.numPages, Math.max(1, NumCast(this.props.Document._curPage, 1))), }; - this._coverPath = "http://cs.brown.edu/~bcz/face.gif";//href.startsWith(window.location.origin) ? await Networking.PostToServer("/thumbnail", params) : { width: 100, height: 100, path: "" }; } runInAction(() => this._showWaiting = true); this.props.startupLive && this.setupPdfJsViewer(); @@ -207,17 +200,6 @@ export class PDFViewer extends React.Component { this.props.setPdfViewer(this); await this.initialLoad(); - this._disposers.filterScript = reaction( - () => ScriptCast(this.props.Document.filterScript), - action(scriptField => { - const oldScript = this._script.originalScript; - this._script = scriptField?.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript; - if (this._script.originalScript !== oldScript) { - this.Index = -1; - } - }), - { fireImmediately: true }); - this.createPdfViewer(); } @@ -232,7 +214,7 @@ export class PDFViewer extends React.Component { () => Math.abs(NumCast(this.props.Document._scrollTop)), (pos) => { if (!this._ignoreScroll) { - (this._showCover || this._showWaiting) && this.setupPdfJsViewer(); + this._showWaiting && this.setupPdfJsViewer(); const viewTrans = quickScroll ?? StrCast(this.props.Document._viewTransition); const durationMiliStr = viewTrans.match(/([0-9]*)ms/); const durationSecStr = viewTrans.match(/([0-9.]*)s/); @@ -377,6 +359,7 @@ export class PDFViewer extends React.Component { this.props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; + this.isAnnotating = true; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { this._textSelecting = false; document.addEventListener("pointermove", this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called @@ -393,6 +376,7 @@ export class PDFViewer extends React.Component { @action finishMarquee = (x?: number, y?: number) => { + this.isAnnotating = false; this._marqueeing = undefined; this._textSelecting = true; document.removeEventListener("pointermove", this.onSelectMove); @@ -402,6 +386,7 @@ export class PDFViewer extends React.Component { @action onSelectEnd = (e: PointerEvent): void => { + this.isAnnotating = false; clearStyleSheetRules(PDFViewer._annotationStyle); this.props.select(false); document.removeEventListener("pointermove", this.onSelectMove); @@ -459,20 +444,6 @@ export class PDFViewer extends React.Component { setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; - getCoverImage = () => { - if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) { - setTimeout((() => { - this.props.Document._height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; - Doc.SetNativeWidth(this.props.Document, (Doc.NativeWidth(this.props.Document) || 0) * this._coverPath.height / this._coverPath.width); - }).bind(this), 0); - } - const nativeWidth = Doc.NativeWidth(this.props.Document); - const nativeHeight = Doc.NativeHeight(this.props.Document); - const resolved = Utils.prepend(this._coverPath.path); - return this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)} - style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; - } - @action onZoomWheel = (e: React.WheelEvent) => { if (this.props.isContentActive(true)) { @@ -495,7 +466,7 @@ export class PDFViewer extends React.Component { } @computed get overlayInfo() { - return !this._overlayAnnoInfo || this._overlayAnnoInfo.author === Doc.CurrentUserEmail ? (null) : + return !this._overlayAnnoInfo ? (null) :
users.user.email === this._overlayAnnoInfo!.author)?.userColor }}> {this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)} @@ -517,41 +488,76 @@ export class PDFViewer extends React.Component { } return this.props.styleProvider?.(doc, props, property); } + renderAnnotations = (docFilters?: () => string[]) => + ; + + @computed get overlayLayerClickableAnnotations() { + return SnappingManager.GetIsDragging() ? (null) : this.renderAnnotations(); + } + @computed get overlayLayerOpaqueAnnotations() { + trace(); + return + } + @computed get overlayLayerTransparentAnnotations() { + trace(); + return + } @computed get overlayLayer() { - const renderAnnotations = (docFilters?: () => string[]) => - ; - return
-
- {renderAnnotations(this.transparentFilter)} + return
+
+ {this.overlayLayerTransparentAnnotations}
-
anno.mixBlendMode) ? "hard-light" : undefined, - transform: `scale(${this._zoomed})` - }}> - {renderAnnotations(this.opaqueFilter)} - {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()} +
anno.mixBlendMode) ? "hard-light" : undefined, }}> + {this.overlayLayerOpaqueAnnotations} + {this.overlayLayerClickableAnnotations}
; } @@ -559,12 +565,6 @@ export class PDFViewer extends React.Component { return
; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } - @computed get standinViews() { - return <> - {this._showCover ? this.getCoverImage() : (null)} - {this._showWaiting ? : (null)} - ; - } contentZoom = () => this._zoomed; savedAnnotations = () => this._savedAnnotations; render() { @@ -581,7 +581,7 @@ export class PDFViewer extends React.Component { {this.annotationLayer} {this.overlayLayer} {this.overlayInfo} - {this.standinViews} + {this._showWaiting ? : (null)} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : { savedAnnotations={this.savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} - anchorMenuCrop={this.crop} + anchorMenuCrop={this._textSelecting ? undefined : this.crop} />}
; diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 4d5ae1018..e532becb5 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -44,7 +44,6 @@ export const documentSchema = createSchema({ _showCaption: "string", // whether editable caption text is overlayed at the bottom of the document _showTitle: "string", // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";". Use :hover suffix to indicate title should be shown on hover _showAudio: "boolean", // whether to show the audio record icon on documents - _freeformLOD: "boolean", // whether to enable LOD switching for CollectionFreeFormViews _pivotField: "string", // specifies which field key should be used as the timeline/pivot axis _columnsFill: "boolean", // whether documents in a stacking view column should be sized to fill the column _columnsSort: "string", // how a document should be sorted "ascending", "descending", undefined (none) -- cgit v1.2.3-70-g09d2 From c31bb0d5d8c4d42fb5cd97b1582de0cae1b16ca0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 12 May 2022 12:17:12 -0400 Subject: fixed perfromance (flickering, speed) issues with having pointerEvents prop invalidate documents - switched to using a function to avoid flickering on PDFs of annotations in particular. --- src/Utils.ts | 4 +++ src/client/views/InkingStroke.tsx | 2 +- src/client/views/SidebarAnnos.tsx | 4 +-- src/client/views/StyleProvider.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +++++---- src/client/views/nodes/ComparisonBox.tsx | 4 +-- src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FieldView.tsx | 4 +-- src/client/views/nodes/MapBox/MapBox.tsx | 10 +++++-- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 4 +-- src/client/views/nodes/PDFBox.tsx | 1 - src/client/views/nodes/WebBox.tsx | 12 ++++---- src/client/views/pdf/Annotation.tsx | 6 ++-- src/client/views/pdf/PDFViewer.tsx | 34 ++++------------------ 14 files changed, 46 insertions(+), 59 deletions(-) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 205f9379e..c3c13a555 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -440,6 +440,10 @@ export function returnTrue() { return true; } export function returnFalse() { return false; } +export function returnAll() { return "all"; } + +export function returnNone() { return "none"; } + export function returnVal(val1?: number, val2?: number) { return val1 !== undefined ? val1 : val2 !== undefined ? val2 : 0; } export function returnOne() { return 1; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 5e9990444..3d9c048e5 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -332,7 +332,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15), StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker, - markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0, + markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0, false, downHdlr); const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", "")); // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 04c0565ea..9e939aa19 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -4,7 +4,7 @@ import { Doc, DocListCast, StrListCast, Opt } from "../../fields/Doc"; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, OmitKeys, returnOne, returnTrue, returnZero } from '../../Utils'; +import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import { Transform } from '../util/Transform'; import { CollectionStackingView } from './collections/CollectionStackingView'; @@ -147,7 +147,7 @@ export class SidebarAnnos extends React.Component { renderDepth={this.props.renderDepth + 1} viewType={CollectionViewType.Stacking} fieldKey={this.sidebarKey} - pointerEvents={"all"} + pointerEvents={returnAll} />
; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index ca9826ab9..5056dedaf 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -204,7 +204,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt this.props.isSelected() || this.props.isContentActive(); + pointerEvents = () => { + const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); + const pointerEvents = this.props.isContentActive() === false ? "none" : this.backgroundActive || this.props.childPointerEvents ? "all" : + (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.(); + return pointerEvents; + } getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; - const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); return ; @@ -1733,7 +1737,7 @@ export class CollectionFreeFormView extends CollectionSubView + pointerEvents={returnNone} /> {clearButton(which)} : // placeholder image if doc is missing
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 02ea87103..e117be0c2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -148,7 +148,7 @@ export interface DocumentViewSharedProps { ignoreAutoHeight?: boolean; forceAutoHeight?: boolean; disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over. - pointerEvents?: string; + pointerEvents?: () => Opt; scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document createNewFilterDoc?: () => void; updateFilterDoc?: (doc: Doc) => void; @@ -870,7 +870,7 @@ export class DocumentViewInternal extends DocComponent {!this._retryThumb || !this.thumbShown() ? (null) : diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ae9acfe3f..686b4308b 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { computed } from "mobx"; import { observer } from "mobx-react"; import { DateField } from "../../../fields/DateField"; -import { Doc, Field, FieldResult } from "../../../fields/Doc"; +import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc"; import { List } from "../../../fields/List"; import { WebField } from "../../../fields/URLField"; import { DocumentViewSharedProps } from "./DocumentView"; @@ -25,7 +25,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { setHeight?: (height: number) => void; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) - pointerEvents?: string; + pointerEvents?: () => Opt; fontSize?: number; height?: number; width?: number; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index e7fc38ecd..c0fd8d8a0 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -489,13 +489,17 @@ export class MapBox extends ViewBoxAnnotatableComponent { + return this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? + "all" : + SnappingManager.GetIsDragging() ? + undefined : "none"; + } @computed get annotationLayer() { - const pe = this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : - SnappingManager.GetIsDragging() ? undefined : "none"; return
{this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => )} + fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />)}
; } diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx index 0d5fedb7b..db7dcb09d 100644 --- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx +++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import { Doc } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; -import { emptyFunction, OmitKeys, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, OmitKeys, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentType } from '../../../documents/DocumentTypes'; import { CollectionStackingView } from '../../collections/CollectionStackingView'; @@ -79,7 +79,7 @@ export class MapBoxInfoWindow extends React.Component (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean)} renderDepth={this.props.renderDepth + 1} viewType={CollectionViewType.Stacking} - pointerEvents="all" + pointerEvents={returnAll} />

diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e6e723ef1..35b5e78a8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -416,7 +416,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index f441e5d36..f01dba4d0 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -12,10 +12,9 @@ import { ComputedField } from "../../../fields/ScriptField"; import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types"; import { ImageField, WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from "../../../Utils"; +import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { KeyCodes } from "../../util/KeyCodes"; import { ScriptingGlobals } from "../../util/ScriptingGlobals"; import { SnappingManager } from "../../util/SnappingManager"; import { undoBatch } from "../../util/UndoManager"; @@ -648,7 +647,7 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}> @@ -658,10 +657,9 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - )} + )}
; } @@ -703,9 +701,9 @@ export class WebBox extends ViewBoxAnnotatableComponent !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; + pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; render() { - const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined; + const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents?.() as any; const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const scale = previewScale * (this.props.scaling?.() || 1); const renderAnnotations = (docFilters?: () => string[]) => diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index b1d1d8293..218f37f3d 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -16,7 +16,7 @@ interface IAnnotationProps extends FieldViewProps { dataDoc: Doc; fieldKey: string; showInfo: (anno: Opt) => void; - pointerEvents?: string; + pointerEvents?: () => Opt; } @observer export @@ -28,7 +28,7 @@ export interface IRegionAnnotationProps extends IAnnotationProps { document: Doc; - pointerEvents?: string; + pointerEvents?: () => Opt; } @observer class RegionAnnotation extends React.Component { @@ -98,7 +98,7 @@ class RegionAnnotation extends React.Component { width: NumCast(this.props.document._width), height: NumCast(this.props.document._height), opacity: this._brushed ? 0.5 : undefined, - pointerEvents: this.props.pointerEvents as any, + pointerEvents: this.props.pointerEvents?.() as any, backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor), }} >
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7ad452e54..608ba07ff 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -8,7 +8,7 @@ import { InkTool } from "../../../fields/InkField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, returnFalse, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { SelectionManager } from "../../util/SelectionManager"; @@ -41,7 +41,6 @@ interface IViewerProps extends FieldViewProps { fieldKey: string; pdf: Pdfjs.PDFDocumentProxy; url: string; - startupLive: boolean; loaded?: (nw: number, nh: number, np: number) => void; setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; @@ -89,28 +88,8 @@ export class PDFViewer extends React.Component { @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } componentDidMount = async () => { - // change the address to be the file address of the PNG version of each page - // file address of the pdf - const { url: { href } } = Cast(this.props.dataDoc[this.props.fieldKey], PdfField)!; - const { url: relative } = this.props; - if (relative.includes("/pdfs/")) { - const pathComponents = relative.split("/pdfs/")[1].split("/"); - const coreFilename = pathComponents.pop()!.split(".")[0]; - const params: any = { - coreFilename, - pageNum: Math.min(this.props.pdf.numPages, Math.max(1, NumCast(this.props.Document._curPage, 1))), - }; - if (pathComponents.length) { - params.subtree = `${pathComponents.join("/")}/`; - } - } else { - const params: any = { - coreFilename: relative.split("/")[relative.split("/").length - 1], - pageNum: Math.min(this.props.pdf.numPages, Math.max(1, NumCast(this.props.Document._curPage, 1))), - }; - } runInAction(() => this._showWaiting = true); - this.props.startupLive && this.setupPdfJsViewer(); + this.setupPdfJsViewer(); this._mainCont.current?.addEventListener("scroll", e => (e.target as any).scrollLeft = 0); this._disposers.autoHeight = reaction(() => this.props.layoutDoc._autoHeight, @@ -456,12 +435,11 @@ export class PDFViewer extends React.Component { } } - pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; + pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; @computed get annotationLayer() { - const pe = this.pointerEvents(); return
{this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - )} + )}
; } @@ -536,7 +514,7 @@ export class PDFViewer extends React.Component {
; } @computed get pdfViewerDiv() { - return
; + return
; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } contentZoom = () => this._zoomed; @@ -544,7 +522,7 @@ export class PDFViewer extends React.Component { render() { TraceMobx(); return
-
Date: Wed, 18 May 2022 14:05:39 -0400 Subject: fixed linking bug so that both start and end link can be regions. added zoom to target checkbox for link editing. extracted out a new field pointerEvents so that lockPosition is not automatically conflratd with no pointer events. updated freeformview focus to work with zooming on region selections. --- src/client/documents/Documents.ts | 3 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/PropertiesView.tsx | 37 +++++++++++++++++++--- src/client/views/StyleProvider.tsx | 5 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 24 +++++++------- src/client/views/linking/LinkEditor.scss | 18 +++++++++++ src/client/views/linking/LinkEditor.tsx | 30 +++++++++++++++--- src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/ImageBox.tsx | 6 ++-- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 4 ++- src/client/views/pdf/PDFViewer.tsx | 29 +++++++++-------- 12 files changed, 120 insertions(+), 44 deletions(-) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 797a129c4..2dd8de089 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -173,6 +173,7 @@ export class DocumentOptions { label?: string; hidden?: boolean; _hidden?: boolean; + pointerEvents?: string; // pointer events that the documentview should have mediaState?: string; // status of media document: "pendingRecording", "recording", "paused", "playing" autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline. dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. @@ -482,7 +483,7 @@ export namespace Docs { }], [DocumentType.MARKER, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { links: "@links(self)", hideLinkButton: true } + options: { links: "@links(self)", hideLinkButton: true, pointerEvents: "none" } }], [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: defaultDataKey }, diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 9b95f1525..ffa168f6b 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -192,7 +192,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
{"Set onClick to follow primary link"}
}>
this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false, false)))}> + onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index bba2ac211..bcfd2dd56 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1227,6 +1227,13 @@ export class PropertiesView extends React.Component { setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))); } + toggleZoomToTarget1 = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom))); + } + toggleZoomToTarget2 = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom))); + } + @computed get editRelationship() { return {

Information

-
+

Link Relationship

{this.editRelationship}
-
+

Description

{this.editDescription}

Behavior

-
+

Follow

-
+

Auto-move anchor

-
+

Display arrow

+
+

Zoom to target

+ +
+
+

Zoom to source

+ +
; } diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 334415b38..93da2fa19 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -55,6 +55,7 @@ function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === function toggleLockedPosition(doc: Doc) { UndoManager.RunInBatch(() => runInAction(() => { doc._lockedPosition = !doc._lockedPosition; + doc._pointerEvents = doc._lockedPosition ? "none" : undefined; }), "toggleBackground"); } @@ -194,10 +195,10 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { const layoutdoc = Doc.Layout(doc); - const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1); - const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[WidthSym](), NumCast(doc.y) + layoutdoc[HeightSym]()); - const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1] }; - const cx = NumCast(this.layoutDoc._panX); - const cy = NumCast(this.layoutDoc._panY); - const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 }; + const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] }; if (scale) { const maxZoom = 5; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context + const newScale = Math.min(maxZoom, 1 / (this.contentScaling || 1) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height))); return { - panX: (bounds.left + bounds.right) / 2, - panY: (bounds.top + bounds.bot) / 2, - scale: Math.min(maxZoom, scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0]), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]))) + panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2, + panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2, + scale: newScale }; } + const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1); + const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1); + const cx = NumCast(this.layoutDoc._panX); + const cy = NumCast(this.layoutDoc._panY); + const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 }; if ((screen.right - screen.left) < (bounds.right - bounds.left) || (screen.bot - screen.top) < (bounds.bot - bounds.top)) { return { @@ -1990,6 +1991,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component { if (!didMove) { diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index abd413f57..1d6496d3c 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -60,6 +60,24 @@ } } +.linkEditor-zoomFollow { + padding-left: 26px; + padding-right: 6.5px; + padding-bottom: 3.5px; + display: flex; + + .linkEditor-zoomFollow-label { + text-decoration-color: black; + color: black; + line-height: 1.7; + } + + .linkEditor-zoomFollow-input { + display: block; + width: 20px; + } +} + .linkEditor-description { padding-left: 26px; padding-right: 6.5px; diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index db331bb75..c3e0aff11 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -9,7 +9,6 @@ import { undoBatch } from "../../util/UndoManager"; import './LinkEditor.scss'; import { LinkRelationshipSearch } from "./LinkRelationshipSearch"; import React = require("react"); -import { ToString } from "../../../fields/FieldSymbols"; interface LinkEditorProps { @@ -23,6 +22,7 @@ export class LinkEditor extends React.Component { @observable description = Field.toString(LinkManager.currentLink?.description as any as Field); @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); + @observable zoomFollow1 = StrCast(this.props.sourceDoc.followLinkZoom); @observable openDropdown: boolean = false; @observable showInfo: boolean = false; @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } @@ -143,9 +143,9 @@ export class LinkEditor extends React.Component { @action handleDescriptionChange = (e: React.ChangeEvent) => { this.description = e.target.value; } @action - handleRelationshipChange = (e: React.ChangeEvent) => { - this.relationship = e.target.value; - } + handleRelationshipChange = (e: React.ChangeEvent) => { this.relationship = e.target.value; } + @action + handleZoomFollowChange = (e: React.ChangeEvent) => { this.props.sourceDoc.followLinkZoom = e.target.checked; } @action handleRelationshipSearchChange = (result: string) => { this.setRelationshipValue(result); @@ -183,6 +183,27 @@ export class LinkEditor extends React.Component {
; } + @computed + get editZoomFollow() { + //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS + return
+
Zoom To Link Target:
+
+
+ +
+
+
; + } @computed get editDescription() { @@ -303,6 +324,7 @@ export class LinkEditor extends React.Component { {this.editDescription} {this.editRelationship} + {this.editZoomFollow} {this.followingDropdown}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b6a2fae1a..49c2761b2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -605,7 +605,7 @@ export class DocumentViewInternal extends DocComponent, zoom: boolean, setPushpin: boolean): void => { + toggleFollowLink = (location: Opt, zoom?: boolean, setPushpin?: boolean): void => { this.Document.ignoreClick = false; if (setPushpin) { this.Document.isPushpin = !this.Document.isPushpin; @@ -614,7 +614,7 @@ export class DocumentViewInternal extends DocComponent) => Opt = () => undefined; @observable _curSuffix = ""; @observable _uploadIcon = uploadIcons.idle; @@ -62,7 +63,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations); + const anchor = this._getAnchor?.(this._savedAnnotations); anchor && this.addDocument(anchor); return anchor ?? this.rootDoc; } @@ -366,6 +367,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { + this._getAnchor = AnchorMenu.Instance?.GetAnchor; this._marqueeing = undefined; this.props.select(false); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 35b5e78a8..cbe7a5cc6 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -198,7 +198,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { const anchor = - AnchorMenu.Instance?.GetAnchor() ?? + this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? Docs.Create.TextanchorDocument({ title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)), y: NumCast(this.layoutDoc._scrollTop), diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 445df8ddd..2112c1d44 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -52,6 +52,7 @@ export class WebBox extends ViewBoxAnnotatableComponent = React.createRef(); private _keyInput = React.createRef(); private _initialScroll: Opt = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)); + private _getAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; private _sidebarRef = React.createRef(); private _searchRef = React.createRef(); private _searchString = ""; @@ -256,7 +257,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { const anchor = - AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? + this._getAnchor(this._savedAnnotations) ?? Docs.Create.WebanchorDocument(this._url, { title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop), y: NumCast(this.layoutDoc._scrollTop), @@ -555,6 +556,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { + this._getAnchor = AnchorMenu.Instance?.GetAnchor; this._marqueeing = undefined; this._isAnnotating = false; this._iframeClick = undefined; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b0a5fc93b..305b1fe68 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,7 +6,6 @@ import { Doc, DocListCast, Field, HeightSym, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, returnFalse, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; @@ -59,7 +58,6 @@ export class PDFViewer extends React.Component { @observable private _marqueeing: number[] | undefined; @observable private _textSelecting = true; @observable private _showWaiting = true; - @observable private _zoomed = 1; @observable private _overlayAnnoInfo: Opt; @observable private Index: number = -1; @@ -70,6 +68,7 @@ export class PDFViewer extends React.Component { private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); + public _getAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; _mainCont: React.RefObject = React.createRef(); private _selectionText: string = ""; private _downX: number = 0; @@ -184,7 +183,7 @@ export class PDFViewer extends React.Component { pagesinit = () => { if (this._pdfViewer._setDocumentViewerElement?.offsetParent) { - runInAction(() => this._pdfViewer.currentScaleValue = this._zoomed = 1); + runInAction(() => this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1); this.gotoPage(NumCast(this.props.Document._curPage, 1)); } document.removeEventListener("pagesinit", this.pagesinit); @@ -355,6 +354,7 @@ export class PDFViewer extends React.Component { @action finishMarquee = (x?: number, y?: number) => { + this._getAnchor = AnchorMenu.Instance?.GetAnchor; this.isAnnotating = false; this._marqueeing = undefined; this._textSelecting = true; @@ -387,13 +387,14 @@ export class PDFViewer extends React.Component { const rect = clientRects.item(i); if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) { const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; + const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1); const annoBox = document.createElement("div"); annoBox.className = "marqueeAnnotator-annotationBox"; // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / this._zoomed + this._mainCont.current.scrollTop).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / this._zoomed).toString(); - annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / this._zoomed).toString(); - annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / this._zoomed).toString(); + annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / pdfScale + this._mainCont.current.scrollTop).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / pdfScale).toString(); + annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / pdfScale).toString(); + annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / pdfScale).toString(); this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top)); } } @@ -430,14 +431,14 @@ export class PDFViewer extends React.Component { if (e.ctrlKey) { const curScale = Number(this._pdfViewer.currentScaleValue); this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000)); - this._zoomed = Number(this._pdfViewer.currentScaleValue); + this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue); } } } pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; @computed get annotationLayer() { - return
+ return
{this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => )}
; @@ -453,7 +454,7 @@ export class PDFViewer extends React.Component { } showInfo = action((anno: Opt) => this._overlayAnnoInfo = anno); - overlayTransform = () => this.scrollXf().scale(1 / this._zoomed); + overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1)); panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (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); basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")]; @@ -498,7 +499,7 @@ export class PDFViewer extends React.Component { style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none", mixBlendMode: "multiply", - transform: `scale(${this._zoomed})` + transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }}> {this.overlayTransparentAnnotations}
@@ -506,7 +507,7 @@ export class PDFViewer extends React.Component { style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none", mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined, - transform: `scale(${this._zoomed})` + transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }}> {this.overlayOpaqueAnnotations} {this.overlayClickableAnnotations} @@ -517,7 +518,7 @@ export class PDFViewer extends React.Component { return
; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } - contentZoom = () => this._zoomed; + contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1); savedAnnotations = () => this._savedAnnotations; render() { TraceMobx(); @@ -525,7 +526,7 @@ export class PDFViewer extends React.Component {
600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`, transform: `scale(${this.contentScaling})` }} > -- cgit v1.2.3-70-g09d2 From d3e2c8d1be275ed04e0b5164960c3fc60334ce8f Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 5 Jun 2022 01:55:26 -0400 Subject: fixed pdf search and page inputs to not iconify with Enter. fixed lightbox to change icons when fitwidth is clicked. --- src/client/views/LightboxView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/PDFBox.tsx') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 3d2769f66..9f890ffad 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -284,7 +284,7 @@ export class LightboxView extends React.Component {
{ e.stopPropagation(); LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; }}> - +
; } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index cbe7a5cc6..f2ca6c96e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -320,7 +320,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> @@ -342,6 +345,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent 99 ? 4 : 3}ch`, pointerEvents: "all" }} onChange={e => this.Document._curPage = Number(e.currentTarget.value)} + onKeyDown={e => e.stopPropagation()} onClick={action(() => this._pageControls = !this._pageControls)} /> {this._pageControls ? pageBtns : (null)}
-- cgit v1.2.3-70-g09d2