aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf')
-rw-r--r--src/client/views/pdf/Annotation.scss1
-rw-r--r--src/client/views/pdf/Annotation.tsx54
-rw-r--r--src/client/views/pdf/PDFMenu.tsx113
-rw-r--r--src/client/views/pdf/PDFViewer.scss14
-rw-r--r--src/client/views/pdf/PDFViewer.tsx185
5 files changed, 213 insertions, 154 deletions
diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss
index cc326eb93..8b242854d 100644
--- a/src/client/views/pdf/Annotation.scss
+++ b/src/client/views/pdf/Annotation.scss
@@ -3,4 +3,5 @@
user-select: none;
position: absolute;
background-color: rgba(146, 245, 95, 0.467);
+ transition: opacity 0.5s;
} \ No newline at end of file
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d29b638e6..84b14cd61 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,42 +1,46 @@
import React = require("react");
import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, FieldValue, BoolCast, NumCast, StrCast, PromiseValue } from "../../../fields/Types";
import { DocumentManager } from "../../util/DocumentManager";
-import PDFMenu from "./PDFMenu";
+import { PDFMenu } from "./PDFMenu";
import "./Annotation.scss";
+import { undoBatch } from "../../util/UndoManager";
interface IAnnotationProps {
anno: Doc;
addDocTab: (document: Doc, where: string) => boolean;
- pinToPres: (document: Doc) => void;
+ pinToPres: (document: Doc, unpin?: boolean) => void;
focus: (doc: Doc) => void;
dataDoc: Doc;
fieldKey: string;
+ showInfo: (anno: Opt<Doc>) => void;
}
@observer
-export default
+export
class Annotation extends React.Component<IAnnotationProps> {
render() {
- return DocListCast(this.props.anno.annotations).map(a => (
- <RegionAnnotation {...this.props} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />));
+ return DocListCast(this.props.anno.annotations).map(a =>
+ <RegionAnnotation {...this.props} showInfo={this.props.showInfo} pinToPres={this.props.pinToPres} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />);
}
}
interface IRegionAnnotationProps {
+ anno: Doc;
x: number;
y: number;
width: number;
height: number;
addDocTab: (document: Doc, where: string) => boolean;
- pinToPres: (document: Doc) => void;
+ pinToPres: (document: Doc, unpin: boolean) => void;
document: Doc;
dataDoc: Doc;
fieldKey: string;
+ showInfo: (anno: Opt<Doc>) => void;
}
@observer
@@ -82,26 +86,37 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
pinToPres = () => {
const group = FieldValue(Cast(this.props.document.group, Doc));
- group && this.props.pinToPres(group);
+ const isPinned = group && Doc.isDocPinned(group) ? true : false;
+ group && this.props.pinToPres(group, isPinned);
}
+ @undoBatch
+ makePushpin = action(() => {
+ const group = Cast(this.props.document.group, Doc, null);
+ group.isPushpin = !group.isPushpin;
+ });
+
+ isPushpin = () => BoolCast(Cast(this.props.document.group, Doc, null).isPushpin);
+
@action
- onPointerDown = async (e: React.PointerEvent) => {
+ onPointerDown = (e: React.PointerEvent) => {
if (e.button === 2 || e.ctrlKey) {
PDFMenu.Instance.Status = "annotation";
PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
PDFMenu.Instance.Pinned = false;
PDFMenu.Instance.AddTag = this.addTag.bind(this);
PDFMenu.Instance.PinToPres = this.pinToPres;
+ PDFMenu.Instance.MakePushpin = this.makePushpin;
+ PDFMenu.Instance.IsPushpin = this.isPushpin;
PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
}
else if (e.button === 0) {
- const annoGroup = await Cast(this.props.document.group, Doc);
- if (annoGroup) {
- DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined);
- e.stopPropagation();
- }
+ e.persist();
+ e.stopPropagation();
+ PromiseValue(this.props.document.group).then(annoGroup => annoGroup instanceof Doc &&
+ DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined)
+ );
}
}
@@ -116,16 +131,17 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
return false;
}
+ @observable _showInfo = false;
render() {
- return (<div className="pdfAnnotation" onPointerDown={this.onPointerDown} ref={this._mainCont}
+ return (<div className="pdfAnnotation" onPointerEnter={action(() => this.props.showInfo(this.props.anno))} onPointerLeave={action(() => this.props.showInfo(undefined))} onPointerDown={this.onPointerDown} ref={this._mainCont}
style={{
top: this.props.y,
left: this.props.x,
width: this.props.width,
height: this.props.height,
- opacity: this._brushed ? 0.5 : undefined,
+ opacity: !this._showInfo && this._brushed ? 0.5 : undefined,
backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor),
- transition: "opacity 0.5s",
- }} />);
+ }} >
+ </div>);
}
} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index bee282d9b..1f8872e3d 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -1,17 +1,17 @@
import React = require("react");
-import "./PDFMenu.scss";
-import { observable, action, computed, } from "mobx";
-import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { unimplementedFunction, returnFalse, Utils } from "../../../Utils";
-import AntimodeMenu, { AntimodeMenuProps } from "../AntimodeMenu";
-import { Doc, Opt } from "../../../fields/Doc";
+import { Tooltip } from "@material-ui/core";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
import { ColorState } from "react-color";
+import { Doc, Opt } from "../../../fields/Doc";
+import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from "../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu";
-
+import "./PDFMenu.scss";
@observer
-export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
+export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: PDFMenu;
private _commentCont = React.createRef<HTMLButtonElement>();
@@ -46,6 +46,8 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
public Delete: () => void = unimplementedFunction;
public AddTag: (key: string, value: string) => boolean = returnFalse;
public PinToPres: () => void = unimplementedFunction;
+ public MakePushpin: () => void = unimplementedFunction;
+ public IsPushpin: () => boolean = returnFalse;
public Marquee: { left: number; top: number; width: number; height: number; } | undefined;
public get Active() { return this._left > 0; }
@@ -57,31 +59,10 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
}
pointerDown = (e: React.PointerEvent) => {
- document.removeEventListener("pointermove", this.pointerMove);
- document.addEventListener("pointermove", this.pointerMove);
- document.removeEventListener("pointerup", this.pointerUp);
- document.addEventListener("pointerup", this.pointerUp);
-
- e.stopPropagation();
- e.preventDefault();
- }
-
- pointerMove = (e: PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
-
- if (!this._dragging) {
+ setupMoveUpEvents(this, e, (e: PointerEvent) => {
this.StartDrag(e, this._commentCont.current!);
- this._dragging = true;
- }
- }
-
- pointerUp = (e: PointerEvent) => {
- this._dragging = false;
- document.removeEventListener("pointermove", this.pointerMove);
- document.removeEventListener("pointerup", this.pointerUp);
- e.stopPropagation();
- e.preventDefault();
+ return true;
+ }, returnFalse, returnFalse);
}
@action
@@ -93,7 +74,7 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get highlighter() {
const button =
- <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onPointerDown={this.highlightClicked}>
+ <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>;
@@ -112,12 +93,13 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
</div>
</div>;
return (
- <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} />
+ <Tooltip key="highlighter" title={<div className="dash-tooltip">{"Click to Highlight"}</div>}>
+ <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
+ </Tooltip>
);
}
- @action
- changeHighlightColor = (color: string, e: React.PointerEvent) => {
+ @action changeHighlightColor = (color: string, e: React.PointerEvent) => {
const col: ColorState = {
hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" },
rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "",
@@ -127,25 +109,11 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
this.highlightColor = Utils.colorString(col);
}
- deleteClicked = (e: React.PointerEvent) => {
- this.Delete();
- }
-
- @action
- keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._keyValue = e.currentTarget.value;
- }
-
- @action
- valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._valueValue = e.currentTarget.value;
- }
-
- @action
- addTag = (e: React.PointerEvent) => {
+ @action keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => { this._keyValue = e.currentTarget.value; };
+ @action valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => { this._valueValue = e.currentTarget.value; };
+ @action addTag = (e: React.PointerEvent) => {
if (this._keyValue.length > 0 && this._valueValue.length > 0) {
this._added = this.AddTag(this._keyValue, this._valueValue);
-
setTimeout(action(() => this._added = false), 1000);
}
}
@@ -154,19 +122,34 @@ export default class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
const buttons = this.Status === "pdf" ?
[
this.highlighter,
- <button key="2" className="antimodeMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
- <FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+
+ <Tooltip key="annotate" title={<div className="dash-tooltip">{"Drag to Place Annotation"}</div>}>
+ <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: "grab" }}>
+ <FontAwesomeIcon icon="comment-alt" size="lg" />
+ </button>
+ </Tooltip>,
] : [
- <button key="5" className="antimodeMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}>
- <FontAwesomeIcon icon="trash-alt" size="lg" /></button>,
- <button key="6" className="antimodeMenu-button" title="Pin to Presentation" onPointerDown={this.PinToPres}>
- <FontAwesomeIcon icon="map-pin" size="lg" /></button>,
- <div key="7" className="pdfMenu-addTag" >
- <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
- <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
- </div>,
- <button key="8" className="antimodeMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
- <FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" /></button>,
+ <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.Delete}>
+ <FontAwesomeIcon icon="trash-alt" size="lg" />
+ </button>
+ </Tooltip>,
+ <Tooltip key="Pin" title={<div className="dash-tooltip">{"Pin to Presentation"}</div>}>
+ <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
+ <FontAwesomeIcon icon="map-pin" size="lg" />
+ </button>
+ </Tooltip>,
+ <Tooltip key="pushpin" title={<div className="dash-tooltip">{"toggle pushpin behavior"}</div>}>
+ <button className="antimodeMenu-button" style={{ color: this.IsPushpin() ? "black" : "white", backgroundColor: this.IsPushpin() ? "white" : "black" }} onPointerDown={this.MakePushpin}>
+ <FontAwesomeIcon icon="thumbtack" size="lg" />
+ </button>
+ </Tooltip>,
+ // <div key="7" className="pdfMenu-addTag" >
+ // <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
+ // <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
+ // </div>,
+ // <button key="8" className="antimodeMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
+ // <FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" /></button>,
];
return this.getElement(buttons);
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 86c73bfee..0ecb4dba4 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -2,6 +2,8 @@
.pdfViewerDash, .pdfViewerDash-interactive {
width: 100%;
height: 100%;
+ top: 0;
+ left:0;
position: absolute;
overflow-y: auto;
overflow-x: hidden;
@@ -57,10 +59,22 @@
left: 0px;
display: inline-block;
width:100%;
+ pointer-events: all;
}
.pdfViewerDash-overlay {
pointer-events: none;
}
+
+ .pdfViewerDash-overlayAnno {
+ top: -17px;
+ pointer-events: none;
+ width: max-content;
+ height: 20px;
+ position: absolute;
+ background: #b8b8b8;
+ border-radius: 5px;
+ display: block;
+ }
.pdfViewerDash-annotationLayer {
position: absolute;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f9ae78778..77dd40f2a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,42 +1,47 @@
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, Field } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
-import { createSchema, makeInterface, listSpec } from "../../../fields/Schema";
-import { ScriptField, ComputedField } from "../../../fields/ScriptField";
+import { createSchema, makeInterface } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import { PdfField } from "../../../fields/URLField";
-import { TraceMobx, GetEffectiveAcl } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils";
+import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils, OmitKeys } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
+import { Networking } from "../../Network";
import { DragManager } from "../../util/DragManager";
import { CompiledScript, CompileScript } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
+import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CollectionView } from "../collections/CollectionView";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
-import { DocumentDecorations } from "../DocumentDecorations";
-import Annotation from "./Annotation";
-import PDFMenu from "./PDFMenu";
+import { Annotation } from "./Annotation";
+import { PDFMenu } from "./PDFMenu";
import "./PDFViewer.scss";
+const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import React = require("react");
-import { SnappingManager } from "../../util/SnappingManager";
+import { LinkDocPreview } from "../nodes/LinkDocPreview";
+import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { SharingManager } from "../../util/SharingManager";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
-import { Networking } from "../../Network";
+const _global = (window /* browser */ || global /* node */) as any;
export const pageSchema = createSchema({
- curPage: "number",
+ _curPage: "number",
rotation: "number",
scrollHeight: "number",
serachMatch: "boolean"
@@ -55,8 +60,10 @@ interface IViewerProps {
fieldKey: string;
Document: Doc;
DataDoc?: Doc;
- docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
ContainingCollectionView: Opt<CollectionView>;
+ docFilters: () => string[];
+ docRangeFilters: () => string[];
PanelWidth: () => number;
PanelHeight: () => number;
ContentScaling: () => number;
@@ -70,7 +77,7 @@ interface IViewerProps {
active: (outsideReaction?: boolean) => boolean;
isChildActive: (outsideReaction?: boolean) => boolean;
addDocTab: (document: Doc, where: string) => boolean;
- pinToPres: (document: Doc) => void;
+ pinToPres: (document: Doc, unpin?: boolean) => void;
addDocument?: (doc: Doc) => boolean;
setPdfViewer: (view: PDFViewer) => void;
ScreenToLocalTransform: () => Transform;
@@ -95,18 +102,14 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@observable private _showWaiting = true;
@observable private _showCover = false;
@observable private _zoomed = 1;
+ @observable private _overlayAnnoInfo: Opt<Doc>;
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 _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- private _reactionDisposer?: IReactionDisposer;
- private _selectionReactionDisposer?: IReactionDisposer;
- private _annotationReactionDisposer?: IReactionDisposer;
- private _scrollTopReactionDisposer?: IReactionDisposer;
- private _filterReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer2?: IReactionDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _selectionText: string = "";
@@ -119,8 +122,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
private _viewerIsSetup = false;
@computed get allAnnotations() {
- return DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]).
- filter(anno => this._script.run({ this: anno }, console.log, true).result);
+ return DocUtils.FilterDocs(DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]), this.props.docFilters(), this.props.docRangeFilters(), undefined);
}
@computed get nonDocAnnotations() { return this.allAnnotations.filter(a => a.annotations); }
@@ -134,7 +136,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
const coreFilename = pathComponents.pop()!.split(".")[0];
const params: any = {
coreFilename,
- pageNum: this.Document.curPage || 1,
+ pageNum: Math.min(this.props.pdf.numPages, Math.max(1, this.Document._curPage || 1)),
};
if (pathComponents.length) {
params.subtree = `${pathComponents.join("/")}/`;
@@ -143,20 +145,25 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
} else {
const params: any = {
coreFilename: relative.split("/")[relative.split("/").length - 1],
- pageNum: this.Document.curPage || 1,
+ pageNum: Math.min(this.props.pdf.numPages, Math.max(1, this.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 = this._showCover = true);
+ runInAction(() => this._showWaiting = true);
this.props.startupLive && this.setupPdfJsViewer();
- this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0);
- this._searchReactionDisposer = reaction(() => this.Document.searchMatch,
+ if (this._mainCont.current) {
+ this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0;
+ const observer = new _global.ResizeObserver(action((entries: any) => this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0)));
+ observer.observe(this._mainCont.current);
+ }
+
+ this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.rootDoc),
m => {
- if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
+ if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0);
else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200);
}, { fireImmediately: true });
- this._selectionReactionDisposer = reaction(() => this.props.isSelected(),
+ this._disposers.selected = reaction(() => this.props.isSelected(),
selected => {
if (!selected) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
@@ -166,26 +173,30 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
(SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer();
},
{ fireImmediately: true });
- this._reactionDisposer = reaction(
+ this._disposers.scrollY = reaction(
() => this.Document._scrollY,
(scrollY) => {
if (scrollY !== undefined) {
(this._showCover || this._showWaiting) && this.setupPdfJsViewer();
- this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0));
+ if ((this.props.renderDepth === -1 || (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc)) && this._mainCont.current) {
+ smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0));
+ } else {
+ console.log("Waiting for preview");
+ }
setTimeout(() => this.Document._scrollY = undefined, 1000);
}
},
{ fireImmediately: true }
);
+ this._disposers.curPage = reaction(
+ () => this.Document._curPage,
+ (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
+ { fireImmediately: true }
+ );
}
componentWillUnmount = () => {
- this._reactionDisposer?.();
- this._scrollTopReactionDisposer?.();
- this._annotationReactionDisposer?.();
- this._filterReactionDisposer?.();
- this._selectionReactionDisposer?.();
- this._searchReactionDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
document.removeEventListener("copy", this.copy);
}
@@ -226,11 +237,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this.props.setPdfViewer(this);
await this.initialLoad();
- this._scrollTopReactionDisposer = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
- (stop) => (stop !== undefined && this.layoutDoc._scrollY === undefined && this._mainCont.current) && (this._mainCont.current.scrollTop = stop),
+ this._disposers.scrollTop = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
+ (stop) => {
+ if (stop !== undefined && this.layoutDoc._scrollY === undefined && this._mainCont.current) {
+ (this._mainCont.current.scrollTop = stop);
+ }
+ },
{ fireImmediately: true });
- this._filterReactionDisposer = reaction(
+ this._disposers.filterScript = reaction(
() => Cast(this.Document.filterScript, ScriptField),
action(scriptField => {
const oldScript = this._script.originalScript;
@@ -245,8 +260,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
pagesinit = action(() => {
- this._pdfViewer.currentScaleValue = this._zoomed = 1;
- this.gotoPage(this.Document.curPage || 1);
+ if (this._pdfViewer._setDocumentViewerElement.offsetParent) {
+ this._pdfViewer.currentScaleValue = this._zoomed = 1;
+ this.gotoPage(this.Document._curPage || 1);
+ }
document.removeEventListener("pagesinit", this.pagesinit);
});
@@ -262,7 +279,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
document.addEventListener("copy", this.copy);
const eventBus = new PDFJSViewer.EventBus(true);
eventBus._on("pagesinit", this.pagesinit);
- eventBus._on("pagerendered", action(() => this._showCover = this._showWaiting = false));
+ eventBus._on("pagerendered", action(() => {
+ this._showWaiting = false;
+ }));
const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus });
const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
this._pdfViewer = new PDFJSViewer.PDFViewer({
@@ -282,14 +301,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
makeAnnotationDocument = (color: string): Opt<Doc> => {
if (this._savedAnnotations.size() === 0) return undefined;
- let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
+ // let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
+ let mainAnnoDoc = Docs.Create.FreeformDocument([], { title: "anno", _width: 1, _height: 1 });
let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
const annoDocs: Doc[] = [];
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
if ((this._savedAnnotations.values()[0][0] as any).marqueeing) {
const anno = this._savedAnnotations.values()[0][0];
- const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + this.Document.title });
+ const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color.replace(/[0-9.]*\)/, ".3)"), title: "Annotation on " + this.Document.title });
if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
if (anno.style.top) annoDoc.y = parseInt(anno.style.top);
if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
@@ -313,7 +333,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
annoDocs.push(annoDoc);
anno.remove();
(annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY));
- (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc.width), maxX));
+ (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc._width), maxX));
}));
mainAnnoDocProto.y = Math.max(minY, 0);
@@ -342,7 +362,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
gotoPage = (p: number) => {
- this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+ if (this._pdfViewer?._setDocumentViewerElement?.offsetParent) {
+ this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+ }
}
@action
@@ -360,10 +382,17 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
+ pageDelay: any;
@action
onScroll = (e: React.UIEvent<HTMLElement>) => {
- this.Document._scrollY === undefined && (this.layoutDoc._scrollTop = this._mainCont.current!.scrollTop);
- this._pdfViewer && (this.Document.curPage = this._pdfViewer.currentPageNumber);
+ if (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+ this.Document._scrollY === undefined && (this.layoutDoc._scrollTop = this._mainCont.current!.scrollTop);
+ this.pageDelay && clearTimeout(this.pageDelay);
+ this.pageDelay = setTimeout(() => {
+ this.pageDelay = undefined;
+ this._pdfViewer && (this.Document._curPage = this._pdfViewer.currentPageNumber);
+ }, 250);
+ }
}
// get the page index that the vertical offset passed in is on
@@ -436,11 +465,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
// if alt+left click, drag and annotate
this._downX = e.clientX;
this._downY = e.clientY;
- addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
+ (e.target as any).tagName === "SPAN" && (this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" }));
if ((this.Document._viewScale || 1) !== 1) return;
if ((e.button !== 0 || e.altKey) && this.active(true)) {
this._setPreviewCursor?.(e.clientX, e.clientY, true);
- //e.stopPropagation();
}
this._marqueeing = false;
if (!e.altKey && e.button === 0 && this.active(true)) {
@@ -462,12 +490,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._marqueeHeight = this._marqueeWidth = 0;
this._marqueeing = true;
}
- document.removeEventListener("pointermove", this.onSelectMove);
document.addEventListener("pointermove", this.onSelectMove);
- document.removeEventListener("pointerup", this.onSelectEnd);
document.addEventListener("pointerup", this.onSelectEnd);
+ document.addEventListener("pointerup", this.removeStyle, true);
}
}
+ removeStyle = () => {
+ clearStyleSheetRules(PDFViewer._annotationStyle);
+ document.removeEventListener("pointerup", this.removeStyle);
+ }
@action
onSelectMove = (e: PointerEvent): void => {
@@ -560,7 +591,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
if (PDFMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
- this.highlight("rgba(245, 230, 95, 0.616)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color)
+ this.highlight("rgba(245, 230, 95, 0.75)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color)
}
else {
PDFMenu.Instance.StartDrag = this.startDrag;
@@ -593,24 +624,26 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
clipDoc._width = this.marqueeWidth();
clipDoc._height = this.marqueeHeight();
clipDoc._scrollTop = this.marqueeY();
- const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
+ const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 100, 100);
+ FormattedTextBox.SelectOnLoad = targetDoc[Id];
Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
clipDoc.rootDocument = targetDoc;
- DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
- targetDoc.layoutKey = "layout";
+ // DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
+ // targetDoc.layoutKey = "layout";
// const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
// Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
// const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
// Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped");
- const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
+ const annotationDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
dragComplete: e => {
- if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
- const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
- annotationDoc.isLinkButton = true;
- if (link) link.followLinkLocation = "onRight";
+ if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) {
+ e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
}
+ annotationDoc.isLinkButton = true; // prevents link button fro showing up --- maybe not a good thing?
+ annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document;
+ e.annoDragData && e.annoDragData.linkDocument && e.annoDragData?.linkDropCallback?.({ linkDocument: e.annoDragData.linkDocument });
}
});
}
@@ -660,24 +693,33 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
TraceMobx();
return <div className="pdfViewerDash-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
- <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
+ <Annotation {...this.props} showInfo={this.showInfo} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
}
</div>;
}
+
+ @computed get overlayInfo() {
+ return !this._overlayAnnoInfo || this._overlayAnnoInfo.author === Doc.CurrentUserEmail ? (null) :
+ <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}>
+ <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
+ {this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
+ </div>
+ </div>;
+ }
+
+ showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
@computed get overlayLayer() {
- return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay"
- style={{ transform: `scale(${this._zoomed})` }}>
- <CollectionFreeFormView {...this.props}
+ return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
+ style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}>
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
annotationsKey={this.annotationKey}
setPreviewCursor={this.setPreviewCursor}
PanelHeight={this.panelWidth}
PanelWidth={this.panelHeight}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
dropAction={"alias"}
VisibleHeight={this.visibleHeight}
focus={this.props.focus}
@@ -692,6 +734,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
addDocument={this.addDocument}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
CollectionView={undefined}
ScreenToLocalTransform={this.overlayTransform}
renderDepth={this.props.renderDepth + 1}
@@ -700,7 +744,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
</div>;
}
@computed get pdfViewerDiv() {
- return <div className={"pdfViewerDash-text" + ((!DocumentDecorations.Instance?.Interacting && (this.props.isSelected() || this.props.isChildActive())) ? "-selected" : "")} ref={this._viewer} />;
+ return <div className={"pdfViewerDash-text" + ((this.props.isSelected() || this.props.isChildActive()) ? "-selected" : "")} ref={this._viewer} />;
}
@computed get contentScaling() { return this.props.ContentScaling(); }
@computed get standinViews() {
@@ -729,6 +773,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
{this.pdfViewerDiv}
{this.overlayLayer}
{this.annotationLayer}
+ {this.overlayInfo}
{this.standinViews}
<PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
</div >;