diff options
| author | bobzel <zzzman@gmail.com> | 2021-09-14 16:07:51 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2021-09-14 16:07:51 -0400 |
| commit | 0fa9fd0f1c51c5a121a212635395bdb63ac28cd6 (patch) | |
| tree | e7c98de1591146cf2a50c513a669d5b7308aea46 /src/client/views/pdf | |
| parent | abc5f96945fc0716fb1ccb4c99005bc7b7473086 (diff) | |
| parent | 301652c454f3b74815aa7be2f2159e0a61d14e0b (diff) | |
merged with master
Diffstat (limited to 'src/client/views/pdf')
| -rw-r--r-- | src/client/views/pdf/AnchorMenu.scss | 29 | ||||
| -rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 37 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 66 |
3 files changed, 93 insertions, 39 deletions
diff --git a/src/client/views/pdf/AnchorMenu.scss b/src/client/views/pdf/AnchorMenu.scss index b7afb26a5..6990bdcf1 100644 --- a/src/client/views/pdf/AnchorMenu.scss +++ b/src/client/views/pdf/AnchorMenu.scss @@ -4,6 +4,35 @@ padding: 5px; grid-template-columns: 90px 20px 90px; } +.anchorMenu-highlighter { + padding-right: 5px; + .antimodeMenu-button { + padding: 0; + padding: 0; + padding-right: 0px; + padding-left: 0px; + width: 5px; + } +} +.anchor-color-preview-button { + width: 25px !important; + .anchor-color-preview { + display: flex; + flex-direction: column; + padding-right: 3px; + width: unset !important; + .color-preview { + width: 60%; + top: 80%; + height: 4px; + position: relative; + top: unset; + width: 15px; + margin-top: 5px; + display: block; + } + } +} .color-wrapper { display: flex; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 55816ed52..ad3afb775 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -41,10 +41,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @observable private highlightColor: string = "rgba(245, 230, 95, 0.616)"; @observable private _showLinkPopup: boolean = false; - @observable public _colorBtn = false; @observable public Highlighting: boolean = false; @observable public Status: "marquee" | "annotation" | "" = ""; + public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search + public OnClick: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined; @@ -65,7 +66,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction(() => SelectionManager.Views(), - selected => AnchorMenu.Instance.fadeOut(true)); + selected => { + this._showLinkPopup = false; + AnchorMenu.Instance.fadeOut(true); + }); } pointerDown = (e: React.PointerEvent) => { @@ -80,6 +84,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { if (!this.Highlight(this.highlightColor, false) && this.Pinned) { this.Highlighting = !this.Highlighting; } + AnchorMenu.Instance.fadeOut(true); } @action @@ -91,9 +96,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @computed get highlighter() { const button = - <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}> - <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /> - <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div> + <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}> + <div className="anchor-color-preview" > + <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /> + <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div> + </div> </button>; const dropdownContent = @@ -111,7 +118,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { </div>; return ( <Tooltip key="highlighter" title={<div className="dash-tooltip">{"Click to Highlight"}</div>}> - <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} /> + <div className="anchorMenu-highlighter"> + <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} /> + </div> </Tooltip> ); } @@ -145,14 +154,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { <FontAwesomeIcon icon="comment-alt" size="lg" /> </button> </Tooltip>, - - //NOTE: link popup is currently incomplete - // <Tooltip key="link" title={<div className="dash-tooltip">{"Link selected text to document or URL"}</div>}> - // <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}> - // <FontAwesomeIcon icon="link" size="lg" /> - // </button> - // </Tooltip>, - // <LinkPopup showPopup={this._showLinkPopup} /> + //NOTE: link popup is currently in progress + <Tooltip key="link" title={<div className="dash-tooltip">{"Find document to link to selected text"}</div>}> + <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}> + <FontAwesomeIcon style={{ position: "absolute", transform: "scale(1.5)" }} icon={"search"} size="lg" /> + <FontAwesomeIcon style={{ position: "absolute", transform: "scale(0.5)", transformOrigin: "top left", top: 12, left: 12 }} icon={"link"} size="lg" /> + </button> + </Tooltip>, + <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} /> ] : [ <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}> <button className="antimodeMenu-button" onPointerDown={this.Delete}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index e7911e8f8..d953c6b6c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,14 +2,13 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; -import { createSchema } from "../../../fields/Schema"; import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse } from "../../../Utils"; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; @@ -69,7 +68,7 @@ export class PDFViewer extends React.Component<IViewerProps> { private _pdfViewer: any; private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); @@ -119,9 +118,11 @@ export class PDFViewer extends React.Component<IViewerProps> { this._mainCont.current?.addEventListener("scroll", e => (e.target as any).scrollLeft = 0); this._disposers.autoHeight = reaction(() => this.props.layoutDoc._autoHeight, - () => { - this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]); - this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1)); + autoHeight => { + if (autoHeight) { + this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]); + this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1)); + } }); this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.props.rootDoc), @@ -182,17 +183,18 @@ export class PDFViewer extends React.Component<IViewerProps> { scrollFocus = (doc: Doc, smooth: boolean) => { const mainCont = this._mainCont.current; let focusSpeed: Opt<number>; - if (doc !== this.props.rootDoc && mainCont && this._pdfViewer) { + if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight); if (scrollTo !== undefined) { focusSpeed = 500; - if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo); + if (!this._pdfViewer) this._initialScroll = scrollTo; + else if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { - this._initialScroll = NumCast(doc.y); + this._initialScroll = NumCast(this.props.layoutDoc._scrollTop); } return focusSpeed; } @@ -370,10 +372,11 @@ export class PDFViewer extends React.Component<IViewerProps> { this._downY = e.clientY; if ((this.props.Document._viewScale || 1) !== 1) return; if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { - this._setPreviewCursor?.(e.clientX, e.clientY, true); + this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this.props.select(false); + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { this._textSelecting = false; @@ -381,10 +384,7 @@ export class PDFViewer extends React.Component<IViewerProps> { } else { // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. 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. - // clear out old marquees and initialize menu for new selection - AnchorMenu.Instance.Status = "marquee"; - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); + this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" }); document.addEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointermove", this.onSelectMove); @@ -453,12 +453,12 @@ export class PDFViewer extends React.Component<IViewerProps> { if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - this._setPreviewCursor(e.clientX, e.clientY, false); + this._setPreviewCursor(e.clientX, e.clientY, false, false); } // e.stopPropagation(); // bcz: not sure why this was here. We need to allow the DocumentView to get clicks to process doubleClicks } - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + 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)) { @@ -507,16 +507,12 @@ export class PDFViewer extends React.Component<IViewerProps> { overlayTransform = () => this.scrollXf().scale(1 / this._zoomed); 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); + transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; @computed get overlayLayer() { - return <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} - style={{ - pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, - mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined, - transform: `scale(${this._zoomed})` - }}> + const renderAnnotations = (docFilters?: () => string[]) => <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} isAnnotationOverlay={true} - isContentActive={returnFalse} fieldKey={this.props.fieldKey + "-annotations"} setPreviewCursor={this.setPreviewCursor} PanelHeight={this.panelHeight} @@ -525,10 +521,30 @@ export class PDFViewer extends React.Component<IViewerProps> { select={emptyFunction} ContentScaling={this.contentZoom} bringToFront={emptyFunction} + docFilters={docFilters || this.props.docFilters} + dontRenderDocuments={docFilters ? false : true} CollectionView={undefined} ScreenToLocalTransform={this.overlayTransform} renderDepth={this.props.renderDepth + 1} - childPointerEvents={true} /> + childPointerEvents={true} />; + return <div> + <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} + style={{ + pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, + mixBlendMode: "multiply", + transform: `scale(${this._zoomed})` + }}> + {renderAnnotations(this.transparentFilter)} + </div> + <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} + style={{ + pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, + mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined, + transform: `scale(${this._zoomed})` + }}> + {renderAnnotations(this.opaqueFilter)} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()} + </div> </div>; } @computed get pdfViewerDiv() { |
