aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/PDFViewer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r--src/client/views/pdf/PDFViewer.tsx368
1 files changed, 208 insertions, 160 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8c0aaea00..7000352e7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -7,7 +7,7 @@ import { Dictionary } from "typescript-collections";
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types";
import { emptyFunction } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
@@ -19,6 +19,7 @@ import Page from "./Page";
import "./PDFViewer.scss";
import React = require("react");
import PDFMenu from "./PDFMenu";
+import { UndoManager } from "../../util/UndoManager";
export const scale = 2;
interface IPDFViewerProps {
@@ -90,7 +91,8 @@ class Viewer extends React.Component<IViewerProps> {
@action
componentDidMount = () => {
this._reactionDisposer = reaction(
- () => [this.props.parent.props.active(), this.startIndex, this.endIndex],
+
+ () => [this.props.parent.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0],
async () => {
await this.initialLoad();
this.renderPages();
@@ -114,12 +116,16 @@ class Viewer extends React.Component<IViewerProps> {
let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
this._isPage = Array<string>(this.props.pdf.numPages);
for (let i = 0; i < this.props.pdf.numPages; i++) {
- await this.props.pdf.getPage(i + 1).then(page => runInAction(() =>
- pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }));
+ await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+ // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
+ let x = page.getViewport(scale);
+ pageSizes[i] = { width: x.width, height: x.height };
+ }));
}
runInAction(() =>
Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
- this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages);
+ this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
+ // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
}
}
@@ -132,6 +138,7 @@ class Viewer extends React.Component<IViewerProps> {
makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
let annoDocs: Doc[] = [];
+ let mainAnnoDoc = new Doc();
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
for (let anno of value) {
let annoDoc = new Doc();
@@ -141,6 +148,7 @@ class Viewer extends React.Component<IViewerProps> {
if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
annoDoc.page = key;
annoDoc.target = sourceDoc;
+ annoDoc.group = mainAnnoDoc;
annoDoc.color = color;
annoDoc.type = AnnotationTypes.Region;
annoDocs.push(annoDoc);
@@ -148,13 +156,12 @@ class Viewer extends React.Component<IViewerProps> {
}
});
- let annoDoc = new Doc();
- annoDoc.annotations = new List<Doc>(annoDocs);
+ mainAnnoDoc.annotations = new List<Doc>(annoDocs);
if (sourceDoc) {
- DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
+ DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
}
this._savedAnnotations.clear();
- return annoDoc;
+ return mainAnnoDoc;
}
drop = async (e: Event, de: DragManager.DropEvent) => {
@@ -185,7 +192,7 @@ class Viewer extends React.Component<IViewerProps> {
if (this._isPage[page] !== "none") {
this._isPage[page] = "none";
this._visibleElements[page] = (
- <div key={`placeholder-${page}`} className="pdfviewer-placeholder"
+ <div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder"
style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
);
}
@@ -199,11 +206,11 @@ class Viewer extends React.Component<IViewerProps> {
pdf={this.props.pdf}
page={page}
numPages={this.props.pdf.numPages}
- key={`rendered-${page + 1}`}
+ key={`${this.props.url}-rendered-${page + 1}`}
name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
pageLoaded={this.pageLoaded}
parent={this.props.parent}
- makePin={this.createPinAnnotation}
+ makePin={emptyFunction}
renderAnnotations={this.renderAnnotations}
createAnnotation={this.createAnnotation}
sendAnnotations={this.receiveAnnotations}
@@ -236,7 +243,7 @@ class Viewer extends React.Component<IViewerProps> {
// endIndex: where to end rendering pages
@computed get endIndex(): number {
- return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer);
+ return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + this._pageSizes[0].height) + this._pageBuffer);
}
@action
@@ -280,27 +287,27 @@ class Viewer extends React.Component<IViewerProps> {
return this._savedAnnotations.getValue(page);
}
- createPinAnnotation = (x: number, y: number, page: number): void => {
- let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
- let pinAnno = new Doc();
- pinAnno.x = x;
- pinAnno.y = y + this.getScrollFromPage(page);
- pinAnno.width = pinAnno.height = PinRadius;
- pinAnno.page = page;
- pinAnno.target = targetDoc;
- pinAnno.type = AnnotationTypes.Pin;
- // this._annotations.push(pinAnno);
- let annoDoc = new Doc();
- annoDoc.annotations = new List<Doc>([pinAnno]);
- let annotations = DocListCast(this.props.parent.Document.annotations);
- if (annotations && annotations.length) {
- annotations.push(annoDoc);
- this.props.parent.Document.annotations = new List<Doc>(annotations);
- }
- else {
- this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
- }
- }
+ // createPinAnnotation = (x: number, y: number, page: number): void => {
+ // let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
+ // let pinAnno = new Doc();
+ // pinAnno.x = x;
+ // pinAnno.y = y + this.getScrollFromPage(page);
+ // pinAnno.width = pinAnno.height = PinRadius;
+ // pinAnno.page = page;
+ // pinAnno.target = targetDoc;
+ // pinAnno.type = AnnotationTypes.Pin;
+ // // this._annotations.push(pinAnno);
+ // let annoDoc = new Doc();
+ // annoDoc.annotations = new List<Doc>([pinAnno]);
+ // let annotations = DocListCast(this.props.parent.Document.annotations);
+ // if (annotations && annotations.length) {
+ // annotations.push(annoDoc);
+ // this.props.parent.Document.annotations = new List<Doc>(annotations);
+ // }
+ // else {
+ // this.props.parent.Document.annotations = new List<Doc>([annoDoc]);
+ // }
+ // }
// get the page index that the vertical offset passed in is on
getPageFromScroll = (vOffset: number) => {
@@ -334,26 +341,6 @@ class Viewer extends React.Component<IViewerProps> {
else {
this._savedAnnotations.setValue(page, [div]);
}
- PDFMenu.Instance.StartDrag = this.startDrag;
- }
- }
-
- startDrag = (e: PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- let thisDoc = this.props.parent.Document;
- // document that this annotation is linked to
- let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
- targetDoc.targetPage = Math.min(...this._savedAnnotations.keys());
- let annotationDoc = this.makeAnnotationDocument(targetDoc, 1, "red");
- let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
- if (this._annotationLayer.current) {
- DragManager.StartAnnotationDrag([this._annotationLayer.current], dragData, e.pageX, e.pageY, {
- handlers: {
- dragComplete: action(emptyFunction),
- },
- hideSource: false
- });
}
}
@@ -362,8 +349,8 @@ class Viewer extends React.Component<IViewerProps> {
let res = annotationDocs.map(a => {
let type = NumCast(a.type);
switch (type) {
- case AnnotationTypes.Pin:
- return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ // case AnnotationTypes.Pin:
+ // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
case AnnotationTypes.Region:
return <RegionAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
default:
@@ -394,7 +381,7 @@ class Viewer extends React.Component<IViewerProps> {
}
export enum AnnotationTypes {
- Region, Pin
+ Region
}
interface IAnnotationProps {
@@ -406,132 +393,193 @@ interface IAnnotationProps {
document: Doc;
}
-@observer
-class PinAnnotation extends React.Component<IAnnotationProps> {
- @observable private _backgroundColor: string = "green";
- @observable private _display: string = "initial";
+// @observer
+// class PinAnnotation extends React.Component<IAnnotationProps> {
+// @observable private _backgroundColor: string = "green";
+// @observable private _display: string = "initial";
+
+// private _mainCont: React.RefObject<HTMLDivElement>;
+
+// constructor(props: IAnnotationProps) {
+// super(props);
+// this._mainCont = React.createRef();
+// }
+
+// componentDidMount = () => {
+// let selected = this.props.document.selected;
+// if (!BoolCast(selected)) {
+// runInAction(() => {
+// this._backgroundColor = "red";
+// this._display = "none";
+// });
+// }
+// if (selected) {
+// if (BoolCast(selected)) {
+// runInAction(() => {
+// this._backgroundColor = "green";
+// this._display = "initial";
+// });
+// }
+// else {
+// runInAction(() => {
+// this._backgroundColor = "red";
+// this._display = "none";
+// });
+// }
+// }
+// else {
+// runInAction(() => {
+// this._backgroundColor = "red";
+// this._display = "none";
+// });
+// }
+// }
+
+// @action
+// pointerDown = (e: React.PointerEvent) => {
+// let selected = this.props.document.selected;
+// if (selected && BoolCast(selected)) {
+// this._backgroundColor = "red";
+// this._display = "none";
+// this.props.document.selected = false;
+// }
+// else {
+// this._backgroundColor = "green";
+// this._display = "initial";
+// this.props.document.selected = true;
+// }
+// e.preventDefault();
+// e.stopPropagation();
+// }
+
+// @action
+// doubleClick = (e: React.MouseEvent) => {
+// if (this._mainCont.current) {
+// let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
+// if (annotations && annotations.length) {
+// let index = annotations.indexOf(this.props.document);
+// annotations.splice(index, 1);
+// this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
+// }
+// // this._mainCont.current.childNodes.forEach(e => e.remove());
+// this._mainCont.current.style.display = "none";
+// // if (this._mainCont.current.parentElement) {
+// // this._mainCont.current.remove();
+// // }
+// }
+// e.stopPropagation();
+// }
+
+// render() {
+// let targetDoc = Cast(this.props.document.target, Doc);
+// if (targetDoc instanceof Doc) {
+// return (
+// <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
+// onDoubleClick={this.doubleClick} ref={this._mainCont}
+// style={{
+// top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
+// height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
+// }}>
+// <div style={{
+// position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
+// display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
+// }}>
+// <DocumentView Document={targetDoc}
+// ContainingCollectionView={undefined}
+// ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
+// isTopMost={false}
+// ContentScaling={() => 1}
+// PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
+// PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
+// focus={emptyFunction}
+// selectOnLoad={false}
+// parentActive={this.props.parent.props.parent.props.active}
+// whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
+// bringToFront={emptyFunction}
+// addDocTab={this.props.parent.props.parent.props.addDocTab}
+// />
+// </div>
+// </div >
+// );
+// }
+// return null;
+// }
+// }
+
+class RegionAnnotation extends React.Component<IAnnotationProps> {
+ @observable private _backgroundColor: string = "red";
+ private _reactionDisposer?: IReactionDisposer;
private _mainCont: React.RefObject<HTMLDivElement>;
constructor(props: IAnnotationProps) {
super(props);
+
this._mainCont = React.createRef();
}
- componentDidMount = () => {
- let selected = this.props.document.selected;
- if (!BoolCast(selected)) {
- runInAction(() => {
- this._backgroundColor = "red";
- this._display = "none";
- });
- }
- if (selected) {
- if (BoolCast(selected)) {
- runInAction(() => {
- this._backgroundColor = "green";
- this._display = "initial";
- });
- }
- else {
- runInAction(() => {
- this._backgroundColor = "red";
- this._display = "none";
- });
- }
- }
- else {
- runInAction(() => {
- this._backgroundColor = "red";
- this._display = "none";
- });
- }
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => BoolCast(this.props.document.delete),
+ () => {
+ if (BoolCast(this.props.document.delete)) {
+ if (this._mainCont.current) {
+ this._mainCont.current.style.display = "none";
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
}
- @action
- pointerDown = (e: React.PointerEvent) => {
- let selected = this.props.document.selected;
- if (selected && BoolCast(selected)) {
- this._backgroundColor = "red";
- this._display = "none";
- this.props.document.selected = false;
- }
- else {
- this._backgroundColor = "green";
- this._display = "initial";
- this.props.document.selected = true;
- }
- e.preventDefault();
- e.stopPropagation();
+ componentWillUnmount() {
+ this._reactionDisposer && this._reactionDisposer();
}
- @action
- doubleClick = (e: React.MouseEvent) => {
- if (this._mainCont.current) {
- let annotations = DocListCast(this.props.parent.props.parent.Document.annotations);
- if (annotations && annotations.length) {
- let index = annotations.indexOf(this.props.document);
- annotations.splice(index, 1);
- this.props.parent.props.parent.Document.annotations = new List<Doc>(annotations);
- }
- // this._mainCont.current.childNodes.forEach(e => e.remove());
- this._mainCont.current.style.display = "none";
- // if (this._mainCont.current.parentElement) {
- // this._mainCont.current.remove();
- // }
+ deleteAnnotation = () => {
+ let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group && annotation.indexOf(group) !== -1) {
+ let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+ this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
}
- e.stopPropagation();
- }
- render() {
- let targetDoc = Cast(this.props.document.target, Doc);
- if (targetDoc instanceof Doc) {
- return (
- <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown}
- onDoubleClick={this.doubleClick} ref={this._mainCont}
- style={{
- top: this.props.y * scale - PinRadius / 2, left: this.props.x * scale - PinRadius / 2, width: PinRadius,
- height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor
- }}>
- <div style={{
- position: "absolute", top: "25px", left: "25px", transform: "scale(3)", transformOrigin: "top left",
- display: this._display, width: targetDoc[WidthSym](), height: targetDoc[HeightSym]()
- }}>
- <DocumentView Document={targetDoc}
- ContainingCollectionView={undefined}
- ScreenToLocalTransform={this.props.parent.props.parent.props.ScreenToLocalTransform}
- isTopMost={false}
- ContentScaling={() => 1}
- PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)}
- PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)}
- focus={emptyFunction}
- selectOnLoad={false}
- parentActive={this.props.parent.props.parent.props.active}
- whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged}
- bringToFront={emptyFunction}
- addDocTab={this.props.parent.props.parent.props.addDocTab}
- />
- </div>
- </div >
- );
+ if (group) {
+ let groupAnnotations = DocListCast(group.annotations);
+ groupAnnotations.forEach(anno => anno.delete = true);
}
- return null;
+
+ PDFMenu.Instance.fadeOut(true);
}
-}
-class RegionAnnotation extends React.Component<IAnnotationProps> {
- @observable private _backgroundColor: string = "red";
- onPointerDown = (e: React.MouseEvent) => {
- let targetDoc = Cast(this.props.document.target, Doc, null);
- if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc);
+ // annotateThis = (e: PointerEvent) => {
+ // e.preventDefault();
+ // e.stopPropagation();
+ // // document that this annotation is linked to
+ // let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" });
+ // let group = FieldValue(Cast(this.props.document.group, Doc));
+ // }
+
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ if (e.button === 0) {
+ let targetDoc = Cast(this.props.document.target, Doc, null);
+ if (targetDoc) {
+ DocumentManager.Instance.jumpToDocument(targetDoc);
+ }
+ }
+ if (e.button === 2) {
+ PDFMenu.Instance.Status = "annotation";
+ PDFMenu.Instance.Delete = this.deleteAnnotation;
+ PDFMenu.Instance.Pinned = false;
+ PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
}
}
render() {
return (
- <div className="pdfViewer-annotationBox" onClick={this.onPointerDown}
+ <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}></div>
);
}