aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2021-09-14 16:07:51 -0400
committerbobzel <zzzman@gmail.com>2021-09-14 16:07:51 -0400
commit0fa9fd0f1c51c5a121a212635395bdb63ac28cd6 (patch)
treee7c98de1591146cf2a50c513a669d5b7308aea46 /src/client/views/pdf
parentabc5f96945fc0716fb1ccb4c99005bc7b7473086 (diff)
parent301652c454f3b74815aa7be2f2159e0a61d14e0b (diff)
merged with master
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/AnchorMenu.scss29
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx37
-rw-r--r--src/client/views/pdf/PDFViewer.tsx66
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() {