From 6df3143ead642356b7823946e9710f840f3faa5a Mon Sep 17 00:00:00 2001 From: _stanleyyip <33562077+yipstanley@users.noreply.github.com> Date: Tue, 14 May 2019 16:02:37 -0400 Subject: argh --- src/client/documents/Documents.ts | 3 +- src/client/views/nodes/DocumentContentsView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/pdf/PDFBox2.tsx | 26 ++++ src/client/views/pdf/PDFViewer.tsx | 183 ++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/client/views/pdf/PDFBox2.tsx create mode 100644 src/client/views/pdf/PDFViewer.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 11929455c..654431905 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,6 +33,7 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; +import { PDFBox2 } from "../views/pdf/PDFBox2"; export interface DocumentOptions { x?: number; @@ -136,7 +137,7 @@ export namespace Docs { } function CreatePdfPrototype(): Doc { let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); + { x: 0, y: 0, nativeWidth: 1200, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } function CreateWebPrototype(): Doc { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f404b7bc6..11df9162a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -23,6 +23,7 @@ import { FieldViewProps } from "./FieldView"; import { Without, OmitKeys } from "../../../Utils"; import { Cast, StrCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; +import { PDFBox2 } from "../pdf/PDFBox2"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index cb27b3f1b..55a37883a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,6 +21,7 @@ import { positionSchema } from "./DocumentView"; import { pageSchema } from "./ImageBox"; import { ImageField, PdfField } from "../../../new_fields/URLField"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -354,10 +355,11 @@ export class PDFBox extends DocComponent(PdfDocumen } render() { trace(); + const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
- {this.pdfRenderer} +
); } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx new file mode 100644 index 000000000..c3f5c19d8 --- /dev/null +++ b/src/client/views/pdf/PDFBox2.tsx @@ -0,0 +1,26 @@ +import React = require("react"); +import { FieldViewProps, FieldView } from "../nodes/FieldView"; +import { DocComponent } from "../DocComponent"; +import { makeInterface } from "../../../new_fields/Schema"; +import { positionSchema } from "../nodes/DocumentView"; +import { pageSchema } from "../nodes/ImageBox"; +import { PDFViewer } from "./PDFViewer"; +import { RouteStore } from "../../../server/RouteStore"; +import { InkingControl } from "../InkingControl"; +import { observer } from "mobx-react"; + +type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; +const PdfDocument = makeInterface(positionSchema, pageSchema); + +@observer +export class PDFBox2 extends DocComponent(PdfDocument) { + public static LayoutString() { return FieldView.LayoutString(PDFBox2); } + + render() { + const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); + return ( + + ) + } +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx new file mode 100644 index 000000000..558a4fab6 --- /dev/null +++ b/src/client/views/pdf/PDFViewer.tsx @@ -0,0 +1,183 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { observable, action, runInAction } from "mobx"; +import { RouteStore } from "../../../server/RouteStore"; +import * as Pdfjs from "pdfjs-dist"; +import { Opt } from "../../../new_fields/Doc"; + +interface IPDFViewerProps { + url: string; +} + +@observer +export class PDFViewer extends React.Component { + @observable _pdf: Opt; + + @action + componentDidMount() { + // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + const pdfUrl = this.props.url; + let promise = Pdfjs.getDocument(pdfUrl).promise; + + promise.then((pdf: Pdfjs.PDFDocumentProxy) => { + runInAction(() => this._pdf = pdf); + }); + } + + render() { + console.log("PDFVIEWER"); + console.log(this._pdf); + return ( +
+ +
+ ); + } +} + +interface IViewerProps { + pdf: Opt; +} + +class Viewer extends React.Component { + render() { + console.log("VIEWER"); + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + console.log(numPages); + return ( +
+ {/* {Array.from(Array(numPages).keys()).map((i) => ( */} + + {/* ))} */} +
+ ); + } +} + +interface IPageProps { + pdf: Opt; + name: string; + numPages: number; +} + +@observer +class Page extends React.Component { + @observable _state: string = "N/A"; + @observable _width: number = 0; + @observable _height: number = 0; + @observable _page: Opt; + canvas: React.RefObject; + textLayer: React.RefObject; + @observable _currPage: number = 1; + + constructor(props: IPageProps) { + super(props); + this.canvas = React.createRef(); + this.textLayer = React.createRef(); + } + + componentDidMount() { + console.log(this.props.pdf); + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + componentDidUpdate() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + private update = (pdf: Pdfjs.PDFDocumentProxy) => { + if (pdf) { + this.loadPage(pdf); + } + else { + this._state = "loading"; + } + } + + private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + if (this._state === "rendering" || this._page) return; + + pdf.getPage(this._currPage).then( + (page: Pdfjs.PDFPageProxy) => { + console.log("PAGE"); + console.log(page); + this._state = "rendering"; + this.renderPage(page); + } + ) + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy) => { + let scale = 1; + let viewport = page.getViewport(scale); + let canvas = this.canvas.current; + if (canvas) { + let context = canvas.getContext("2d"); + canvas.width = viewport.width; + this._width = viewport.width; + canvas.height = viewport.height; + this._height = viewport.height; + if (context) { + page.render({ canvasContext: context, viewport: viewport }); + page.getTextContent().then((res: Pdfjs.TextContent) => { + //@ts-ignore + let textLayer = Pdfjs.renderTextLayer({ + textContent: res, + container: this.textLayer.current, + viewport: viewport + }); + // textLayer._render(); + this._state = "rendered"; + }); + + this._page = page; + } + } + } + + @action + prevPage = (e: React.MouseEvent) => { + if (this._currPage > 2 && this._state !== "rendering") { + this._currPage = Math.max(this._currPage - 1, 1); + this._page = undefined; + this.loadPage(this.props.pdf!); + this._state = "rendering"; + } + } + + @action + nextPage = (e: React.MouseEvent) => { + if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") { + this._currPage = Math.min(this._currPage + 1, this.props.numPages) + this._page = undefined; + this.loadPage(this.props.pdf!); + this._state = "rendering"; + } + } + + render() { + return ( +
+
+ +
+
+
+
<
+
>
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f043c60ff537667021a76e716b6e6465aec44525 Mon Sep 17 00:00:00 2001 From: _stanleyyip <33562077+yipstanley@users.noreply.github.com> Date: Sun, 19 May 2019 12:30:09 -0700 Subject: scrolling ? --- src/client/views/pdf/PDFViewer.scss | 19 +++++++++++++++++++ src/client/views/pdf/PDFViewer.tsx | 29 ++++++++++++++++------------- 2 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 src/client/views/pdf/PDFViewer.scss (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss new file mode 100644 index 000000000..d8ff06406 --- /dev/null +++ b/src/client/views/pdf/PDFViewer.scss @@ -0,0 +1,19 @@ +.canvasContainer {} + +.viewer-button-cont { + position: absolute; + display: flex; + justify-content: space-evenly; + align-items: center; +} + +.viewer-previousPage, +.viewer-nextPage { + background: grey; + font-weight: bold; + opacity: 0.5; + padding: 0 10px; + border-radius: 5px; +} + +.viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 558a4fab6..1b445eae4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,6 +4,7 @@ import { observable, action, runInAction } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; +import "./PDFViewer.scss"; interface IPDFViewerProps { url: string; @@ -46,15 +47,16 @@ class Viewer extends React.Component { console.log(numPages); return (
- {/* {Array.from(Array(numPages).keys()).map((i) => ( */} - - {/* ))} */} + {Array.from(Array(numPages).keys()).map((i) => ( + + ))} }
); } @@ -64,6 +66,7 @@ interface IPageProps { pdf: Opt; name: string; numPages: number; + page: number; } @observer @@ -74,7 +77,7 @@ class Page extends React.Component { @observable _page: Opt; canvas: React.RefObject; textLayer: React.RefObject; - @observable _currPage: number = 1; + @observable _currPage: number = this.props.page + 1; constructor(props: IPageProps) { super(props); @@ -172,11 +175,11 @@ class Page extends React.Component {
-
-
+
+ {/*
<
>
-
+
*/}
); } -- cgit v1.2.3-70-g09d2 From 2cc62cd88688ccdec8275fcaaba939d448f9baf5 Mon Sep 17 00:00:00 2001 From: Stanley Date: Sun, 19 May 2019 13:56:30 -0700 Subject: ugh --- src/client/views/nodes/PDFBox.tsx | 18 +++++++++--------- src/client/views/pdf/PDFBox2.tsx | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 55a37883a..bf3f299bc 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -70,15 +70,15 @@ export class PDFBox extends DocComponent(PdfDocumen @computed private get thumbnailPage() { return Cast(this.props.Document.thumbnailPage, "number", -1); } componentDidMount() { - this._reactionDisposer = reaction( - () => [SelectionManager.SelectedDocuments().slice()], - () => { - if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) { - this.saveThumbnail(); - this._interactive = true; - } - }, - { fireImmediately: true }); + // this._reactionDisposer = reaction( + // () => [SelectionManager.SelectedDocuments().slice()], + // () => { + // if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage !== this.thumbnailPage && !this.props.isSelected()) { + // this.saveThumbnail(); + // this._interactive = true; + // } + // }, + // { fireImmediately: true }); } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx index c3f5c19d8..71825c260 100644 --- a/src/client/views/pdf/PDFBox2.tsx +++ b/src/client/views/pdf/PDFBox2.tsx @@ -8,6 +8,7 @@ import { PDFViewer } from "./PDFViewer"; import { RouteStore } from "../../../server/RouteStore"; import { InkingControl } from "../InkingControl"; import { observer } from "mobx-react"; +import { trace } from "mobx"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -17,6 +18,7 @@ export class PDFBox2 extends DocComponent(PdfDocume public static LayoutString() { return FieldView.LayoutString(PDFBox2); } render() { + trace(); const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); return ( -- cgit v1.2.3-70-g09d2 From 3a9f1a40cb6bcd35783aa83e66c3e253812aa39d Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sun, 19 May 2019 19:10:44 -0400 Subject: stacking ! --- src/client/documents/Documents.ts | 7 +- src/client/views/nodes/PDFBox.tsx | 296 ++----------------------------------- src/client/views/pdf/PDFViewer.tsx | 31 +--- 3 files changed, 21 insertions(+), 313 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5752bb096..2df733fd5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,12 +33,9 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -<<<<<<< HEAD import { PDFBox2 } from "../views/pdf/PDFBox2"; -======= import { schema } from "prosemirror-schema-basic"; import { UndoManager } from "../util/UndoManager"; ->>>>>>> 01a223f2e6685506cc1e5db69e9062d5ff0d3246 export interface DocumentOptions { x?: number; @@ -173,8 +170,8 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(), + { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } function CreateWebPrototype(): Doc { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 9b0207d0c..d74dc53c4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -4,8 +4,8 @@ import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; //@ts-ignore -import { Document, Page } from "react-pdf"; -import 'react-pdf/dist/Page/AnnotationLayer.css'; +// import { Document, Page } from "react-pdf"; +// import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { Utils } from '../../../Utils'; import { Annotation } from './Annotation'; @@ -14,7 +14,7 @@ import "./PDFBox.scss"; import React = require("react"); import { SelectionManager } from "../../util/SelectionManager"; import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, HeightSym, Doc } from "../../../new_fields/Doc"; import { DocComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; import { positionSchema } from "./DocumentView"; @@ -53,300 +53,26 @@ const PdfDocument = makeInterface(positionSchema, pageSchema); export class PDFBox extends DocComponent(PdfDocument) { public static LayoutString() { return FieldView.LayoutString(PDFBox); } - private _mainDiv = React.createRef(); - private renderHeight = 2400; - - @observable private _renderAsSvg = true; @observable private _alt = false; - - private _reactionDisposer?: IReactionDisposer; - - @observable private _perPageInfo: Object[] = []; //stores pageInfo - @observable private _pageInfo: any = { area: [], divs: [], anno: [] }; //divs is array of objects linked to anno - - @observable private _currAnno: any = []; @observable private _interactive: boolean = false; - @observable private _loaded: boolean = false; - - @computed private get curPage() { return NumCast(this.Document.curPage, 1); } - @computed private get thumbnailPage() { return NumCast(this.props.Document.thumbnailPage, -1); } - - componentDidMount() { - let wasSelected = false; - this._reactionDisposer = reaction( - () => this.props.isSelected(), - () => { - if (this.curPage > 0 && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { - this.saveThumbnail(); - } - wasSelected = this._interactive = this.props.isSelected(); - }, - { fireImmediately: true }); - - } - - componentWillUnmount() { - if (this._reactionDisposer) this._reactionDisposer(); - } - - /** - * highlighting helper function - */ - makeEditableAndHighlight = (colour: string) => { - var range, sel = window.getSelection(); - if (sel && sel.rangeCount && sel.getRangeAt) { - range = sel.getRangeAt(0); - } - document.designMode = "on"; - if (!document.execCommand("HiliteColor", false, colour)) { - document.execCommand("HiliteColor", false, colour); - } - - if (range && sel) { - sel.removeAllRanges(); - sel.addRange(range); - - let obj: Object = { parentDivs: [], spans: [] }; - //@ts-ignore - if (range.commonAncestorContainer.className === 'react-pdf__Page__textContent') { //multiline highlighting case - obj = this.highlightNodes(range.commonAncestorContainer.childNodes); - } else { //single line highlighting case - let parentDiv = range.commonAncestorContainer.parentElement; - if (parentDiv) { - if (parentDiv.className === 'react-pdf__Page__textContent') { //when highlight is overwritten - obj = this.highlightNodes(parentDiv.childNodes); - } else { - parentDiv.childNodes.forEach((child) => { - if (child.nodeName === 'SPAN') { - //@ts-ignore - obj.parentDivs.push(parentDiv); - //@ts-ignore - child.id = "highlighted"; - //@ts-ignore - obj.spans.push(child); - // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler - } - }); - } - } - } - this._pageInfo.divs.push(obj); - - } - document.designMode = "off"; - } - - highlightNodes = (nodes: NodeListOf) => { - let temp = { parentDivs: [], spans: [] }; - nodes.forEach((div) => { - div.childNodes.forEach((child) => { - if (child.nodeName === 'SPAN') { - //@ts-ignore - temp.parentDivs.push(div); - //@ts-ignore - child.id = "highlighted"; - //@ts-ignore - temp.spans.push(child); - // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler - } - }); - - }); - return temp; - } - - /** - * when the cursor enters the highlight, it pops out annotation. ONLY WORKS FOR SINGLE DIV LINES - */ - @action - onEnter = (e: any) => { - let span: HTMLSpanElement = e.toElement; - let index: any; - this._pageInfo.divs.forEach((obj: any) => { - obj.spans.forEach((element: any) => { - if (element === span && !index) { - index = this._pageInfo.divs.indexOf(obj); - } - }); - }); - - if (this._pageInfo.anno.length >= index + 1) { - if (this._currAnno.length === 0) { - this._currAnno.push(this._pageInfo.anno[index]); - } - } else { - if (this._currAnno.length === 0) { //if there are no current annotation - let div = span.offsetParent; - //@ts-ignore - let divX = div.style.left; - //@ts-ignore - let divY = div.style.top; - //slicing "px" from the end - divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span) - divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span) - let annotation = ; - this._pageInfo.anno.push(annotation); - this._currAnno.push(annotation); - } - } - - } - /** - * highlight function for highlighting actual text. This works fine. - */ - highlight = (color: string) => { - if (window.getSelection()) { - try { - if (!document.execCommand("hiliteColor", false, color)) { - this.makeEditableAndHighlight(color); - } - } catch (ex) { - this.makeEditableAndHighlight(color); - } + getHeight = (): number => { + if (this.props.Document) { + let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log(doc); + return NumCast(doc.height); } + return 0; } - /** - * controls the area highlighting (stickies) Kinda temporary - */ - onPointerDown = (e: React.PointerEvent) => { - if (this.props.isSelected() && !InkingControl.Instance.selectedTool && e.buttons === 1) { - if (e.altKey) { - this._alt = true; - } else { - if (e.metaKey) { - e.stopPropagation(); - } - } - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - if (this.props.isSelected() && e.buttons === 2) { - runInAction(() => this._alt = true); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - } - - /** - * controls area highlighting and partially highlighting. Kinda temporary - */ - @action - onPointerUp = (e: PointerEvent) => { - this._alt = false; - document.removeEventListener("pointerup", this.onPointerUp); - if (this.props.isSelected()) { - this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color. - } - this._interactive = true; - } - - - @action - saveThumbnail = () => { - this._renderAsSvg = false; - setTimeout(() => { - let nwidth = FieldValue(this.Document.nativeWidth, 0); - let nheight = FieldValue(this.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) - .then(action((dataUrl: string) => { - this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); - this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); - this._renderAsSvg = true; - })) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); - }); - }, 1250); - } - - @action - onLoaded = (page: any) => { - // bcz: the number of pages should really be set when the document is imported. - this.props.Document.numPages = page._transport.numPages; - if (this._perPageInfo.length === 0) { //Makes sure it only runs once - this._perPageInfo = [...Array(page._transport.numPages)]; - } - this._loaded = true; - } - - @action - setScaling = (r: any) => { - // bcz: the nativeHeight should really be set when the document is imported. - // also, the native dimensions could be different for different pages of the canvas - // so this design is flawed. - var nativeWidth = FieldValue(this.Document.nativeWidth, 0); - if (!FieldValue(this.Document.nativeHeight, 0)) { - var nativeHeight = nativeWidth * r.offset.height / r.offset.width; - this.props.Document.height = nativeHeight / nativeWidth * FieldValue(this.Document.width, 0); - this.props.Document.nativeHeight = nativeHeight; - } - } - @computed - get pdfPage() { - return ; - } - @computed - get pdfContent() { - trace(); - let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if (!pdfUrl) { - return

No pdf url to render

; - } - let pdfpage = this.pdfPage; - let body = this.Document.nativeHeight ? - pdfpage : - - {({ measureRef }) => -
- {pdfpage} -
- } -
; - let xf = (this.Document.nativeHeight || 0) / this.renderHeight; - return
- - {body} - -
; - } - - @computed - get pdfRenderer() { - let proxy = this._loaded ? (null) : this.imageProxyRenderer; - let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if ((!this._interactive && proxy) || !pdfUrl) { - return proxy; - } - return [ - this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element), - this._currAnno.map((element: any) => element), - this.pdfContent, - proxy - ]; - } - - @computed - get imageProxyRenderer() { - let thumbField = this.props.Document.thumbnail; - if (thumbField) { - let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - return ; - } - return (null); - } - @action onKeyDown = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = true); - @action onKeyUp = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = false); render() { trace(); const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
+
e.stopPropagation()} className={classname}> -
+
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1b445eae4..26becebf1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -5,6 +5,7 @@ import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; +import "pdfjs-dist/web/pdf_viewer.css"; interface IPDFViewerProps { url: string; @@ -52,8 +53,8 @@ class Viewer extends React.Component { pdf={this.props.pdf} page={i} numPages={numPages} - key={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`} - name={`${this.props.pdf ? this.props.pdf.fingerprint + `page${i + 1}` : "undefined"}`} + key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} + name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} {...this.props} /> ))} } @@ -149,24 +150,12 @@ class Page extends React.Component { } } - @action - prevPage = (e: React.MouseEvent) => { - if (this._currPage > 2 && this._state !== "rendering") { - this._currPage = Math.max(this._currPage - 1, 1); - this._page = undefined; - this.loadPage(this.props.pdf!); - this._state = "rendering"; - } + onPointerDown = (e: React.PointerEvent) => { + e.stopPropagation(); } - @action - nextPage = (e: React.MouseEvent) => { - if (this._currPage < this.props.numPages - 1 && this._state !== "rendering") { - this._currPage = Math.min(this._currPage + 1, this.props.numPages) - this._page = undefined; - this.loadPage(this.props.pdf!); - this._state = "rendering"; - } + onPointerMove = (e: React.PointerEvent) => { + e.stopPropagation(); } render() { @@ -175,11 +164,7 @@ class Page extends React.Component {
-
- {/*
-
<
-
>
-
*/} +
); } -- cgit v1.2.3-70-g09d2 From e815a45a50e215e1a5c3ada6404226326a8530a9 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 20 May 2019 04:02:23 -0400 Subject: YES --- src/client/views/nodes/PDFBox.tsx | 21 ++++- src/client/views/pdf/PDFViewer.scss | 4 + src/client/views/pdf/PDFViewer.tsx | 165 +++++++++++++++++++++++++++++++++--- 3 files changed, 176 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index d74dc53c4..62c8e1c99 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -54,7 +54,7 @@ export class PDFBox extends DocComponent(PdfDocumen public static LayoutString() { return FieldView.LayoutString(PDFBox); } @observable private _alt = false; - @observable private _interactive: boolean = false; + @observable private _scrollY: number = 0; getHeight = (): number => { if (this.props.Document) { @@ -65,13 +65,28 @@ export class PDFBox extends DocComponent(PdfDocumen return 0; } + loaded = (nw: number, nh: number) => { + if (this.props.Document) { + let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + doc.nativeWidth = nw; + doc.nativeHeight = nh; + } + } + + @action + onScroll = (e: React.UIEvent) => { + if (e.currentTarget) { + this._scrollY = e.currentTarget.scrollTop; + } + } + render() { trace(); const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> - +
e.stopPropagation()} className={classname}> +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index d8ff06406..3a6045317 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -16,4 +16,8 @@ border-radius: 5px; } +.textLayer { + user-select: auto; +} + .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 26becebf1..6201f6330 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,14 +1,18 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, computed } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; +import { number } from "prop-types"; +import { JSXElement } from "babel-types"; interface IPDFViewerProps { url: string; + loaded: (nw: number, nh: number) => void; + scrollY: number; } @observer @@ -27,11 +31,9 @@ export class PDFViewer extends React.Component { } render() { - console.log("PDFVIEWER"); - console.log(this._pdf); return (
- +
); } @@ -39,25 +41,166 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; + loaded: (nw: number, nh: number) => void; + scrollY: number; } +type PDFItem = React.Component | HTMLDivElement; + +@observer class Viewer extends React.Component { + @observable.shallow private _visibleElements: JSX.Element[] = []; + @observable private _isPage: boolean[] = []; + @observable private _pageSizes: { width: number, height: number }[] = []; + @observable private _startIndex: number = 0; + @observable private _endIndex: number = 1; + @observable private _loaded: boolean = false; + @observable private _pdf: Opt; + + private _pageBuffer: number = 1; + + @computed get scrollY(): number { + return this.props.scrollY; + } + + @computed get startIndex(): number { + return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer); + } + + @computed get endIndex(): number { + let width = this._pageSizes.map(i => i.width); + return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); + } + + componentDidMount = () => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); + } + + componentDidUpdate = (prevProps: IViewerProps) => { + if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { + this._pdf = this.props.pdf; + this.renderPages(this.startIndex, this.endIndex); + } + } + + @action + renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + + if (this._visibleElements.length !== numPages) { + let divs = Array.from(Array(numPages).keys()).map(i => ( + + )); + let arr = Array.from(Array(numPages).keys()).map(i => false); + this._visibleElements.push(...divs); + this._isPage.push(...arr); + } + + if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) { + return; + } + + for (let i = startIndex; i <= endIndex; i++) { + if (this._isPage[i] && forceRender) { + this._visibleElements[i] = ( + + ); + this._isPage[i] = true; + } + else if (!this._isPage[i]) { + this._visibleElements[i] = ( + + ); + this._isPage[i] = true; + } + } + + for (let i = 0; i < numPages; i++) { + if (i < startIndex || i > endIndex) { + if (this._isPage[i]) { + this._visibleElements[i] = ( +
+ ); + this._isPage[i] = false; + } + } + } + + return; + } + + getIndex = (vOffset: number) => { + if (this._loaded) { + let index = 0; + let currOffset = vOffset; + while (currOffset - this._pageSizes[index].height > 0) { + currOffset -= this._pageSizes[index].height; + index++; + } + return index; + } + return 0; + } + + @action + pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.props.loaded(page.width, page.height); + if (index > this._pageSizes.length) { + this._pageSizes.push({ width: page.width, height: page.height }); + } + else { + this._pageSizes[index - 1] = { width: page.width, height: page.height }; + } + if (index === numPages) { + this._loaded = true; + let divs = Array.from(Array(numPages).keys()).map(i => ( +
+ )); + this._visibleElements = new Array(...divs); + } + } + render() { - console.log("VIEWER"); + console.log(`START: ${this.startIndex}`); + console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - console.log(numPages); return (
- {Array.from(Array(numPages).keys()).map((i) => ( + {/* {Array.from(Array(numPages).keys()).map((i) => ( - ))} } + ))} */} + {this._visibleElements}
); } @@ -68,6 +211,7 @@ interface IPageProps { name: string; numPages: number; page: number; + pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; } @observer @@ -113,12 +257,10 @@ class Page extends React.Component { pdf.getPage(this._currPage).then( (page: Pdfjs.PDFPageProxy) => { - console.log("PAGE"); - console.log(page); this._state = "rendering"; this.renderPage(page); } - ) + ); } @action @@ -132,6 +274,7 @@ class Page extends React.Component { this._width = viewport.width; canvas.height = viewport.height; this._height = viewport.height; + this.props.pageLoaded(this._currPage, viewport); if (context) { page.render({ canvasContext: context, viewport: viewport }); page.getTextContent().then((res: Pdfjs.TextContent) => { -- cgit v1.2.3-70-g09d2 From 1fb7a7bc185c1ba9bbe0f21ad5e16cf19235b2da Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 13:59:04 -0400 Subject: updatesss --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 6 +++- src/client/views/pdf/PDFViewer.tsx | 59 +++++++++++++++++++++++++++++-------- 3 files changed, 53 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 62c8e1c99..dd945b030 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -86,7 +86,7 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> - +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 3a6045317..9d41a1bb0 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,4 +1,8 @@ -.canvasContainer {} +.textLayer { + div { + user-select: text; + } +} .viewer-button-cont { position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6201f6330..d510ba91c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,18 +1,24 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction, computed } from "mobx"; +import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; +import * as htmlToImage from "html-to-image"; import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { number } from "prop-types"; import { JSXElement } from "babel-types"; +import { PDFBox } from "../nodes/PDFBox"; +import { NumCast, FieldValue } from "../../../new_fields/Types"; +import { SearchBox } from "../SearchBox"; +import { Utils } from "../../../Utils"; interface IPDFViewerProps { url: string; loaded: (nw: number, nh: number) => void; scrollY: number; + parent: PDFBox; } @observer @@ -33,7 +39,7 @@ export class PDFViewer extends React.Component { render() { return (
- +
); } @@ -43,10 +49,9 @@ interface IViewerProps { pdf: Opt; loaded: (nw: number, nh: number) => void; scrollY: number; + parent: PDFBox; } -type PDFItem = React.Component | HTMLDivElement; - @observer class Viewer extends React.Component { @observable.shallow private _visibleElements: JSX.Element[] = []; @@ -56,8 +61,41 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; + @observable private _renderAsSvg = true; private _pageBuffer: number = 1; + private _reactionDisposer?: IReactionDisposer; + private _mainDiv = React.createRef(); + + @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); } + + componentDidMount() { + let wasSelected = this.props.parent.props.isSelected(); + this._reactionDisposer = reaction( + () => [this.props.parent.props.isSelected(), this.startIndex], + () => { + if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) { + this.saveThumbnail(); + } + wasSelected = this.props.parent.props.isSelected(); + }, + { fireImmediately: true } + ); + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); + } + + saveThumbnail = () => { + this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1); + this._renderAsSvg = false; + setTimeout(() => { + let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); + let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0); + htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) + .then(action((dataUrl: string) => { + })); + }, 1250); + } @computed get scrollY(): number { return this.props.scrollY; @@ -72,11 +110,6 @@ class Viewer extends React.Component { return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); } - componentDidMount = () => { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); - } - componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; @@ -188,7 +221,7 @@ class Viewer extends React.Component { console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return ( -
+
{/* {Array.from(Array(numPages).keys()).map((i) => ( { } onPointerDown = (e: React.PointerEvent) => { + console.log("down"); e.stopPropagation(); } onPointerMove = (e: React.PointerEvent) => { + console.log("move") e.stopPropagation(); } render() { return ( -
+
-
+
); } -- cgit v1.2.3-70-g09d2 From e93304523b10f7c6da85cebdfae05a3b1b6eb84c Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 14:03:51 -0400 Subject: merge^ --- src/client/documents/Documents.ts | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index be7356a09..973c90ff4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,11 +33,6 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -<<<<<<< HEAD -import { PDFBox2 } from "../views/pdf/PDFBox2"; -import { schema } from "prosemirror-schema-basic"; -======= ->>>>>>> 7d3ef1c914cc1cc0b6c05b14773a8b83e1b95c96 import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; var requestImageSize = require('request-image-size'); -- cgit v1.2.3-70-g09d2 From cffe1d2d45e241a21fb071573399200567738163 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 4 Jun 2019 15:45:33 -0400 Subject: additionsss --- src/client/views/nodes/PDFBox.tsx | 10 +----- src/client/views/pdf/PDFViewer.tsx | 63 ++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 83f69f7f9..217d7b5e1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -7,23 +7,15 @@ import Measure from "react-measure"; // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; -import { Utils } from '../../../Utils'; -import { DocServer } from "../../DocServer"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; -import { SearchBox } from "../SearchBox"; -import { Annotation } from './Annotation'; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; -var path = require('path'); import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { Opt, HeightSym, Doc } from "../../../new_fields/Doc"; +import { NumCast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; -import { ImageField, PdfField } from "../../../new_fields/URLField"; import { PDFViewer } from "../pdf/PDFViewer"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d510ba91c..999cb6378 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -13,6 +13,10 @@ import { PDFBox } from "../nodes/PDFBox"; import { NumCast, FieldValue } from "../../../new_fields/Types"; import { SearchBox } from "../SearchBox"; import { Utils } from "../../../Utils"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { DocServer } from "../../DocServer"; +import { ImageField } from "../../../new_fields/URLField"; +var path = require("path"); interface IPDFViewerProps { url: string; @@ -24,6 +28,7 @@ interface IPDFViewerProps { @observer export class PDFViewer extends React.Component { @observable _pdf: Opt; + private _mainDiv = React.createRef(); @action componentDidMount() { @@ -38,8 +43,8 @@ export class PDFViewer extends React.Component { render() { return ( -
- +
+
); } @@ -50,6 +55,7 @@ interface IViewerProps { loaded: (nw: number, nh: number) => void; scrollY: number; parent: PDFBox; + mainCont: React.RefObject; } @observer @@ -61,20 +67,19 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; - @observable private _renderAsSvg = true; + @observable private _renderAsSvg = false; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - private _mainDiv = React.createRef(); - @computed private get thumbnailPage() { return NumCast(this.props.parent.Document.thumbnailPage, -1); } + @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); } - componentDidMount() { + componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { - if (this.startIndex > 0 && !this.props.parent.props.isTopMost && this.startIndex !== this.thumbnailPage && wasSelected && !this.props.parent.props.isSelected()) { + if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } wasSelected = this.props.parent.props.isSelected(); @@ -86,14 +91,23 @@ class Viewer extends React.Component { } saveThumbnail = () => { - this.props.parent.props.Document.thumbnailPage = FieldValue(this.props.parent.Document.curPage, -1); + this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); this._renderAsSvg = false; setTimeout(() => { let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - let nheight = FieldValue(this.props.parent.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) + htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) .then(action((dataUrl: string) => { - })); + SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { + if (returnedFilename) { + let url = DocServer.prepend(returnedFilename); + this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); + } + runInAction(() => this._renderAsSvg = true); + }); + })) + .catch(function (error: any) { + console.error("Oops, something went wrong!", error); + }); }, 1250); } @@ -101,6 +115,29 @@ class Viewer extends React.Component { return this.props.scrollY; } + @computed get imageProxyRenderer() { + let thumbField = this.props.parent.props.Document.thumbnail; + if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) { + let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50; + let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + let field = thumbField; + if (field instanceof ImageField) path = this.choosePath(field.url); + return ; + } + } + + @action onError = () => { + } + + choosePath(url: URL) { + if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) { + return url.href; + } + let ext = path.extname(url.href); + ///TODO: Not done lol - syip2 + return url.href; + } + @computed get startIndex(): number { return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer); } @@ -221,7 +258,7 @@ class Viewer extends React.Component { console.log(`END: ${this.endIndex}`) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return ( -
+
{/* {Array.from(Array(numPages).keys()).map((i) => ( { {...this.props} /> ))} */} - {this._visibleElements} + {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements}
); } -- cgit v1.2.3-70-g09d2 From 1708f2b2a19d3d4efc081bcc4ee82b4d5149da08 Mon Sep 17 00:00:00 2001 From: loudonclear Date: Tue, 4 Jun 2019 19:08:01 -0400 Subject: YES --- package.json | 1 + src/client/views/nodes/PDFBox.tsx | 4 +-- src/client/views/pdf/PDFViewer.tsx | 50 ++++++++++++++++++-------------- src/server/Search.ts | 3 +- src/server/index.ts | 58 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 58b2cb049..648ebdbf9 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "bluebird": "^3.5.3", "body-parser": "^1.18.3", "bootstrap": "^4.3.1", + "canvas": "^2.5.0", "class-transformer": "^0.2.0", "connect-flash": "^0.1.1", "connect-mongo": "^2.0.3", diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 217d7b5e1..7e335e75e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -14,7 +14,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, StrCast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; @@ -77,7 +77,7 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); - const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; + const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 999cb6378..374e598a4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -91,24 +91,24 @@ class Viewer extends React.Component { } saveThumbnail = () => { - this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); - this._renderAsSvg = false; - setTimeout(() => { - let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) - .then(action((dataUrl: string) => { - SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { - if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); - this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); - } - runInAction(() => this._renderAsSvg = true); - }); - })) - .catch(function (error: any) { - console.error("Oops, something went wrong!", error); - }); - }, 1250); + // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); + // this._renderAsSvg = false; + // setTimeout(() => { + // let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); + // htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) + // .then(action((dataUrl: string) => { + // SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { + // if (returnedFilename) { + // let url = DocServer.prepend(returnedFilename); + // this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); + // } + // runInAction(() => this._renderAsSvg = true); + // }); + // })) + // .catch(function (error: any) { + // console.error("Oops, something went wrong!", error); + // }); + // }, 1250); } @computed get scrollY(): number { @@ -338,15 +338,23 @@ class Page extends React.Component { let scale = 1; let viewport = page.getViewport(scale); let canvas = this.canvas.current; - if (canvas) { - let context = canvas.getContext("2d"); + let tempCanvas = document.createElement('canvas') + if (tempCanvas && canvas) { + let ctx = canvas.getContext("2d"); + let context = tempCanvas.getContext("2d"); + tempCanvas.width = viewport.width; canvas.width = viewport.width; this._width = viewport.width; + tempCanvas.height = viewport.height; canvas.height = viewport.height; this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); if (context) { - page.render({ canvasContext: context, viewport: viewport }); + page.render({ canvasContext: context, viewport: viewport }).promise.then(() => { + if (context && ctx) { + ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0); + } + }); page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore let textLayer = Pdfjs.renderTextLayer({ diff --git a/src/server/Search.ts b/src/server/Search.ts index 5ca5578a7..fd6ef36a6 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -7,6 +7,7 @@ export class Search { private url = 'http://localhost:8983/solr/'; public async updateDocument(document: any) { + return; try { const res = await rp.post(this.url + "dash/update", { headers: { 'content-type': 'application/json' }, @@ -14,7 +15,7 @@ export class Search { }); return res; } catch (e) { - console.warn("Search error: " + e + document); + // console.warn("Search error: " + e + document); } } diff --git a/src/server/index.ts b/src/server/index.ts index fd66c90b4..9f95df4e0 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,6 +7,7 @@ import * as expressValidator from 'express-validator'; import * as formidable from 'formidable'; import * as fs from 'fs'; import * as sharp from 'sharp'; +import * as Pdfjs from 'pdfjs-dist'; const imageDataUri = require('image-data-uri'); import * as mobileDetect from 'mobile-detect'; import { ObservableMap } from 'mobx'; @@ -28,6 +29,7 @@ import { MessageStore, Transferable, Types, Diff } from "./Message"; import { RouteStore } from './RouteStore'; const app = express(); const config = require('../../webpack.config'); +import { createCanvas, loadImage, Canvas } from "canvas"; const compiler = webpack(config); const port = 1050; // default port to listen const serverPort = 4321; @@ -168,7 +170,31 @@ addSecureRoute( RouteStore.getCurrUser ); +class NodeCanvasFactory { + create = (width: number, height: number) => { + var canvas = createCanvas(width, height); + var context = canvas.getContext('2d'); + return { + canvas: canvas, + context: context, + }; + } + + reset = (canvasAndContext: any, width: number, height: number) => { + canvasAndContext.canvas.width = width; + canvasAndContext.canvas.height = height; + } + + destroy = (canvasAndContext: any) => { + canvasAndContext.canvas.width = 0; + canvasAndContext.canvas.height = 0; + canvasAndContext.canvas = null; + canvasAndContext.context = null; + } +} + const pngTypes = [".png", ".PNG"]; +const pdfTypes = [".pdf", ".PDF"]; const jpgTypes = [".jpg", ".JPG", ".jpeg", ".JPEG"]; const uploadDir = __dirname + "/public/files/"; // SETTERS @@ -203,6 +229,38 @@ app.post( }); isImage = true; } + else if (pdfTypes.includes(ext)) { + Pdfjs.getDocument(uploadDir + file).promise + .then((pdf: Pdfjs.PDFDocumentProxy) => { + let numPages = pdf.numPages; + let factory = new NodeCanvasFactory(); + for (let pageNum = 0; pageNum < numPages; pageNum++) { + console.log(pageNum); + pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { + console.log("reading " + pageNum); + let viewport = page.getViewport(1); + let canvasAndContext = factory.create(viewport.width, viewport.height); + let renderContext = { + canvasContext: canvasAndContext.context, + viewport: viewport, + canvasFactory: factory + } + console.log("read " + pageNum); + + page.render(renderContext).promise + .then(() => { + console.log("saving " + pageNum); + let stream = canvasAndContext.canvas.createPNGStream(); + let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); + stream.pipe(out); + out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); + }, (reason: string) => { + console.error(reason + ` ${pageNum}`); + }); + }); + } + }); + } if (isImage) { resizers.forEach(resizer => { fs.createReadStream(uploadDir + file).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + resizer.suffix + ext)); -- cgit v1.2.3-70-g09d2 From f774314ddae00f2d2ceda886f0b6699f01f80718 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 16:33:57 -0400 Subject: better virtualization and thumbnails git status --- src/client/views/nodes/PDFBox.tsx | 12 ++- src/client/views/pdf/PDFViewer.scss | 4 + src/client/views/pdf/PDFViewer.tsx | 158 ++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 7e335e75e..6d604ec75 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -14,9 +14,11 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; +import { PdfField } from "../../../new_fields/URLField"; +import { HeightSym, WidthSym } from "../../../new_fields/Doc"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -65,6 +67,7 @@ export class PDFBox extends DocComponent(PdfDocumen let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.nativeWidth = nw; doc.nativeHeight = nh; + doc.height = nh * (doc[WidthSym]() / nw); } } @@ -77,11 +80,12 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); - const pdfUrl = window.origin + RouteStore.corsProxy + "/" + StrCast(this.props.Document.title); + const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); + console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> - +
e.stopPropagation()} className={classname}> +
); } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 9d41a1bb0..42bf023d6 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,4 +24,8 @@ user-select: auto; } +.pdfBox-cont-interactive { + overflow-x: visible; +} + .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 374e598a4..84d9f1ad0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,18 +4,18 @@ import { observable, action, runInAction, computed, IReactionDisposer, reaction import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; import * as htmlToImage from "html-to-image"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, WidthSym } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { number } from "prop-types"; import { JSXElement } from "babel-types"; import { PDFBox } from "../nodes/PDFBox"; -import { NumCast, FieldValue } from "../../../new_fields/Types"; +import { NumCast, FieldValue, Cast } from "../../../new_fields/Types"; import { SearchBox } from "../SearchBox"; import { Utils } from "../../../Utils"; import { Id } from "../../../new_fields/FieldSymbols"; import { DocServer } from "../../DocServer"; -import { ImageField } from "../../../new_fields/URLField"; +import { ImageField, PdfField } from "../../../new_fields/URLField"; var path = require("path"); interface IPDFViewerProps { @@ -44,7 +44,7 @@ export class PDFViewer extends React.Component { render() { return (
- +
); } @@ -56,6 +56,7 @@ interface IViewerProps { scrollY: number; parent: PDFBox; mainCont: React.RefObject; + url: string; } @observer @@ -71,71 +72,63 @@ class Viewer extends React.Component { private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - - @computed private get thumbnailY() { return NumCast(this.props.parent.Document.thumbnailY, -1); } + private _widthReactionDisposer?: IReactionDisposer; + private _width: number = 0; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { - if (this.startIndex >= 0 && !this.props.parent.props.isTopMost && this.scrollY !== this.thumbnailY && wasSelected && !this.props.parent.props.isSelected()) { + if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } + else if (!wasSelected && this.props.parent.props.isSelected()) { + this.renderPages(this.startIndex, this.endIndex, true); + } wasSelected = this.props.parent.props.isSelected(); }, { fireImmediately: true } ); - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); - } - saveThumbnail = () => { - // this.props.parent.props.Document.thumbnailY = FieldValue(this.scrollY, 0); - // this._renderAsSvg = false; - // setTimeout(() => { - // let nwidth = FieldValue(this.props.parent.Document.nativeWidth, 0); - // htmlToImage.toPng(this.props.mainCont.current!, { width: nwidth, height: nwidth, quality: 0.8, }) - // .then(action((dataUrl: string) => { - // SearchBox.convertDataUri(dataUrl, `icon${this.props.parent.Document[Id]}_${this.startIndex}`).then((returnedFilename) => { - // if (returnedFilename) { - // let url = DocServer.prepend(returnedFilename); - // this.props.parent.props.Document.thumbnail = new ImageField(new URL(url)); - // } - // runInAction(() => this._renderAsSvg = true); - // }); - // })) - // .catch(function (error: any) { - // console.error("Oops, something went wrong!", error); - // }); - // }, 1250); - } + // this._widthReactionDisposer = reaction( + // () => [this._docWidth], + // () => { + // if (this._width !== this._docWidth) { + // this._width = this._docWidth; + // this.renderPages(this.startIndex, this.endIndex, true); + // console.log(this._width); + // } + // }, + // { fireImmediately: true } + // ) - @computed get scrollY(): number { - return this.props.scrollY; + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + this.renderPages(0, numPages - 1, true); } - @computed get imageProxyRenderer() { - let thumbField = this.props.parent.props.Document.thumbnail; - if (thumbField && this._renderAsSvg && NumCast(this.props.parent.props.Document.startY, 0) === this.scrollY) { - let pw = typeof this.props.parent.props.PanelWidth === "function" ? this.props.parent.props.PanelWidth() : typeof this.props.parent.props.PanelWidth === "number" ? (this.props.parent.props.PanelWidth as any) as number : 50; - let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - let field = thumbField; - if (field instanceof ImageField) path = this.choosePath(field.url); - return ; + componentWillUnmount = () => { + if (this._reactionDisposer) { + this._reactionDisposer(); } } - @action onError = () => { + @action + saveThumbnail = () => { + const address: string = this.props.url; + console.log(address); + for (let i = 0; i < this._visibleElements.length; i++) { + if (this._isPage[i]) { + let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; + let nWidth = this._pageSizes[i].width; + let nHeight = this._pageSizes[i].height; + this._visibleElements[i] = ; + } + } } - choosePath(url: URL) { - if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) { - return url.href; - } - let ext = path.extname(url.href); - ///TODO: Not done lol - syip2 - return url.href; + @computed get scrollY(): number { + return this.props.scrollY; } @computed get startIndex(): number { @@ -157,6 +150,9 @@ class Viewer extends React.Component { @action renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + if (!this.props.pdf) { + return; + } if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( @@ -178,21 +174,19 @@ class Viewer extends React.Component { return; } - for (let i = startIndex; i <= endIndex; i++) { - if (this._isPage[i] && forceRender) { - this._visibleElements[i] = ( - - ); - this._isPage[i] = true; + for (let i = 0; i < numPages; i++) { + if (i < startIndex || i > endIndex) { + if (this._isPage[i]) { + this._visibleElements[i] = ( +
+ ); + } + this._isPage[i] = false; } - else if (!this._isPage[i]) { + } + + for (let i = startIndex; i <= endIndex; i++) { + if (!this._isPage[i] || forceRender) { this._visibleElements[i] = ( { } } - for (let i = 0; i < numPages; i++) { - if (i < startIndex || i > endIndex) { - if (this._isPage[i]) { - this._visibleElements[i] = ( -
- ); - this._isPage[i] = false; - } - } - } + this._startIndex = startIndex; + this._endIndex = endIndex; return; } getIndex = (vOffset: number) => { if (this._loaded) { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; let index = 0; let currOffset = vOffset; - while (currOffset - this._pageSizes[index].height > 0) { + while (index < numPages && currOffset - this._pageSizes[index].height > 0) { currOffset -= this._pageSizes[index].height; index++; } @@ -236,6 +223,9 @@ class Viewer extends React.Component { @action pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { + if (this._loaded) { + return; + } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; this.props.loaded(page.width, page.height); if (index > this._pageSizes.length) { @@ -270,7 +260,7 @@ class Viewer extends React.Component { {...this.props} /> ))} */} - {this._renderAsSvg ? this.imageProxyRenderer : this._visibleElements} + {this._visibleElements}
); } @@ -338,32 +328,24 @@ class Page extends React.Component { let scale = 1; let viewport = page.getViewport(scale); let canvas = this.canvas.current; - let tempCanvas = document.createElement('canvas') - if (tempCanvas && canvas) { + let textLayer = this.textLayer.current; + if (canvas && textLayer) { let ctx = canvas.getContext("2d"); - let context = tempCanvas.getContext("2d"); - tempCanvas.width = viewport.width; canvas.width = viewport.width; this._width = viewport.width; - tempCanvas.height = viewport.height; canvas.height = viewport.height; this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); - if (context) { - page.render({ canvasContext: context, viewport: viewport }).promise.then(() => { - if (context && ctx) { - ctx.putImageData(context.getImageData(0, 0, tempCanvas.width, tempCanvas.height), 0, 0); - } - }); + if (ctx) { + page.render({ canvasContext: ctx, viewport: viewport }) page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore - let textLayer = Pdfjs.renderTextLayer({ + Pdfjs.renderTextLayer({ textContent: res, - container: this.textLayer.current, + container: textLayer, viewport: viewport }); // textLayer._render(); - this._state = "rendered"; }); this._page = page; -- cgit v1.2.3-70-g09d2 From c4180929255db3bc7a75938e07afd86522f0a15e Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 18:09:39 -0400 Subject: bug fixess --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 4 ---- src/client/views/pdf/PDFViewer.tsx | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6d604ec75..4ee3ae098 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -84,7 +84,7 @@ export class PDFBox extends DocComponent(PdfDocumen console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> +
e.stopPropagation()} className={classname}>
); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 42bf023d6..9d41a1bb0 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,8 +24,4 @@ user-select: auto; } -.pdfBox-cont-interactive { - overflow-x: visible; -} - .viewer {} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 84d9f1ad0..95f31bb89 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -104,7 +104,7 @@ class Viewer extends React.Component { // ) let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.renderPages(0, numPages - 1, true); + setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } componentWillUnmount = () => { -- cgit v1.2.3-70-g09d2 From 727c8be8d51766f483a90edf07366b5840f5a4f6 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 5 Jun 2019 23:00:10 -0400 Subject: screen transform remaining --- src/client/views/pdf/PDFViewer.scss | 6 ++++- src/client/views/pdf/PDFViewer.tsx | 48 ++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 9d41a1bb0..484d9dc04 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -24,4 +24,8 @@ user-select: auto; } -.viewer {} \ No newline at end of file +.pdfViewer-annotationBox { + position: absolute; + background-color: red; + opacity: 0.5; +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 95f31bb89..a76527618 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -354,18 +354,54 @@ class Page extends React.Component { } onPointerDown = (e: React.PointerEvent) => { - console.log("down"); - e.stopPropagation(); + if (e.button === 0) { + e.stopPropagation(); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + } + } + + onPointerMove = (e: PointerEvent) => { + if (e.button === 0) { + e.stopPropagation(); + } } - onPointerMove = (e: React.PointerEvent) => { - console.log("move") - e.stopPropagation(); + onPointerUp = (e: PointerEvent) => { + let sel = window.getSelection(); + if (sel && sel.type === "Range") { + // console.log(sel.getRangeAt(0)); + let commonContainer = sel.getRangeAt(0).commonAncestorContainer; + let startContainer = sel.getRangeAt(0).startContainer; + let endContainer = sel.getRangeAt(0).endContainer; + let clientRects = sel.getRangeAt(0).getClientRects(); + console.log(sel.getRangeAt(0)); + let annoBoxes = []; + if (this.textLayer.current) { + // let transform = Utils.GetScreenTransform(this.textLayer.current); + console.log(transform); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + annoBox.style.top = rect.top.toString(); + annoBox.style.left = rect.left.toString(); + annoBox.style.width = rect.width.toString(); + annoBox.style.height = rect.height.toString(); + // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`; + this.textLayer.current.appendChild(annoBox); + } + } + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); } render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From a37629f55ef279167a5ef2fec88dc548f36f4938 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 6 Jun 2019 14:13:55 -0400 Subject: commentss --- src/client/views/nodes/PDFBox.tsx | 38 +--------- src/client/views/pdf/PDFViewer.tsx | 147 ++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4ee3ae098..de75c67cf 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -20,29 +20,6 @@ import { PDFViewer } from "../pdf/PDFViewer"; import { PdfField } from "../../../new_fields/URLField"; import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx - * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, - * area selection (I call it stickies), embedded ink node for directly annotating using a pen or - * mouse, and pagination. - * - * - * HOW TO USE: - * AREA selection: - * 1) Click on Area button. - * 2) click on any part of the PDF, and drag to get desired sized area shape - * 3) You can write on the area (hence the reason why it's called sticky) - * 4) to make another area, you need to click on area button AGAIN. - * - * HIGHLIGHT: (Buggy. No multiline/multidiv text highlighting for now...) - * 1) just click and drag on a text - * 2) click highlight - * 3) for annotation, just pull your cursor over to that text - * 4) another method: click on highlight first and then drag on your desired text - * 5) To make another highlight, you need to reclick on the button - * - * written by: Andrew Kim - */ - type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -53,15 +30,6 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; - getHeight = (): number => { - if (this.props.Document) { - let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - console.log(doc); - return NumCast(doc.height); - } - return 0; - } - loaded = (nw: number, nh: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; @@ -80,11 +48,13 @@ export class PDFBox extends DocComponent(PdfDocumen render() { trace(); + // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); - console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
e.stopPropagation()} className={classname}> +
e.stopPropagation()} className={classname}>
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index a76527618..d5a0a7aa1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,22 +1,11 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; -import { RouteStore } from "../../../server/RouteStore"; import * as Pdfjs from "pdfjs-dist"; -import * as htmlToImage from "html-to-image"; -import { Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; -import { number } from "prop-types"; -import { JSXElement } from "babel-types"; import { PDFBox } from "../nodes/PDFBox"; -import { NumCast, FieldValue, Cast } from "../../../new_fields/Types"; -import { SearchBox } from "../SearchBox"; -import { Utils } from "../../../Utils"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { DocServer } from "../../DocServer"; -import { ImageField, PdfField } from "../../../new_fields/URLField"; -var path = require("path"); interface IPDFViewerProps { url: string; @@ -25,6 +14,9 @@ interface IPDFViewerProps { parent: PDFBox; } +/** + * Wrapper that loads the PDF and cascades the pdf down + */ @observer export class PDFViewer extends React.Component { @observable _pdf: Opt; @@ -32,7 +24,6 @@ export class PDFViewer extends React.Component { @action componentDidMount() { - // const pdfUrl = window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; const pdfUrl = this.props.url; let promise = Pdfjs.getDocument(pdfUrl).promise; @@ -59,30 +50,35 @@ interface IViewerProps { url: string; } +/** + * Handles rendering and virtualization of the pdf + */ @observer class Viewer extends React.Component { + // _visibleElements is the array of JSX elements that gets rendered @observable.shallow private _visibleElements: JSX.Element[] = []; + // _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder @observable private _isPage: boolean[] = []; @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _startIndex: number = 0; @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; - @observable private _renderAsSvg = false; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; - private _widthReactionDisposer?: IReactionDisposer; - private _width: number = 0; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); + // reaction for when document gets (de)selected this._reactionDisposer = reaction( () => [this.props.parent.props.isSelected(), this.startIndex], () => { + // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); } + // if selected, render pdf else if (!wasSelected && this.props.parent.props.isSelected()) { this.renderPages(this.startIndex, this.endIndex, true); } @@ -91,19 +87,7 @@ class Viewer extends React.Component { { fireImmediately: true } ); - // this._widthReactionDisposer = reaction( - // () => [this._docWidth], - // () => { - // if (this._width !== this._docWidth) { - // this._width = this._docWidth; - // this.renderPages(this.startIndex, this.endIndex, true); - // console.log(this._width); - // } - // }, - // { fireImmediately: true } - // ) - - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + // On load, render pdf setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } @@ -115,13 +99,15 @@ class Viewer extends React.Component { @action saveThumbnail = () => { + // file address of the pdf const address: string = this.props.url; - console.log(address); for (let i = 0; i < this._visibleElements.length; i++) { if (this._isPage[i]) { + // change the address to be the file address of the PNG version of each page let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; let nWidth = this._pageSizes[i].width; let nHeight = this._pageSizes[i].height; + // replace page with image this._visibleElements[i] = ; } } @@ -143,10 +129,16 @@ class Viewer extends React.Component { componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; + // render pages if the scorll position changes this.renderPages(this.startIndex, this.endIndex); } } + /** + * @param startIndex: where to start rendering pages + * @param endIndex: where to end rendering pages + * @param forceRender: (optional), force pdfs to re-render, even if the page already exists + */ @action renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; @@ -154,6 +146,7 @@ class Viewer extends React.Component { return; } + // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( { this._isPage.push(...arr); } + // if nothing changed, return if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) { return; } + // unrender pages outside of the pdf by replacing them with empty stand-in divs for (let i = 0; i < numPages; i++) { if (i < startIndex || i > endIndex) { if (this._isPage[i]) { @@ -185,6 +180,7 @@ class Viewer extends React.Component { } } + // render pages for any indices that don't already have pages (force rerender will make these render regardless) for (let i = startIndex; i <= endIndex; i++) { if (!this._isPage[i] || forceRender) { this._visibleElements[i] = ( @@ -207,6 +203,7 @@ class Viewer extends React.Component { return; } + // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { if (this._loaded) { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; @@ -221,6 +218,10 @@ class Viewer extends React.Component { return 0; } + /** + * Called by the Page class when it gets rendered, initializes the lists and + * puts a placeholder with all of the correct page sizes when all of the pages have been loaded. + */ @action pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { if (this._loaded) { @@ -244,22 +245,8 @@ class Viewer extends React.Component { } render() { - console.log(`START: ${this.startIndex}`); - console.log(`END: ${this.endIndex}`) - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; return (
- {/* {Array.from(Array(numPages).keys()).map((i) => ( - - ))} */} {this._visibleElements}
); @@ -276,22 +263,23 @@ interface IPageProps { @observer class Page extends React.Component { - @observable _state: string = "N/A"; - @observable _width: number = 0; - @observable _height: number = 0; - @observable _page: Opt; - canvas: React.RefObject; - textLayer: React.RefObject; - @observable _currPage: number = this.props.page + 1; + @observable private _state: string = "N/A"; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _page: Opt; + @observable private _currPage: number = this.props.page + 1; + + private _canvas: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _textLayer: React.RefObject; constructor(props: IPageProps) { super(props); - this.canvas = React.createRef(); - this.textLayer = React.createRef(); + this._canvas = React.createRef(); + this._textLayer = React.createRef(); } componentDidMount() { - console.log(this.props.pdf); if (this.props.pdf) { this.update(this.props.pdf); } @@ -327,8 +315,8 @@ class Page extends React.Component { private renderPage = (page: Pdfjs.PDFPageProxy) => { let scale = 1; let viewport = page.getViewport(scale); - let canvas = this.canvas.current; - let textLayer = this.textLayer.current; + let canvas = this._canvas.current; + let textLayer = this._textLayer.current; if (canvas && textLayer) { let ctx = canvas.getContext("2d"); canvas.width = viewport.width; @@ -337,7 +325,9 @@ class Page extends React.Component { this._height = viewport.height; this.props.pageLoaded(this._currPage, viewport); if (ctx) { + // renders the page onto the canvas context page.render({ canvasContext: ctx, viewport: viewport }) + // renders text onto the text container page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore Pdfjs.renderTextLayer({ @@ -345,7 +335,6 @@ class Page extends React.Component { container: textLayer, viewport: viewport }); - // textLayer._render(); }); this._page = page; @@ -358,6 +347,11 @@ class Page extends React.Component { e.stopPropagation(); document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); + if (!e.ctrlKey) { + for (let anno of this._currentAnnotations) { + anno.remove(); + } + } } } @@ -367,30 +361,35 @@ class Page extends React.Component { } } + startAnnotation = (e: DragEvent) => { + console.log("drag starting"); + } + + pointerDownCancel = (e: PointerEvent) => { + e.stopPropagation(); + } + onPointerUp = (e: PointerEvent) => { let sel = window.getSelection(); + // if selecting over a range of things if (sel && sel.type === "Range") { - // console.log(sel.getRangeAt(0)); - let commonContainer = sel.getRangeAt(0).commonAncestorContainer; - let startContainer = sel.getRangeAt(0).startContainer; - let endContainer = sel.getRangeAt(0).endContainer; let clientRects = sel.getRangeAt(0).getClientRects(); - console.log(sel.getRangeAt(0)); - let annoBoxes = []; - if (this.textLayer.current) { - // let transform = Utils.GetScreenTransform(this.textLayer.current); - console.log(transform); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect) { let annoBox = document.createElement("div"); annoBox.className = "pdfViewer-annotationBox"; - annoBox.style.top = rect.top.toString(); - annoBox.style.left = rect.left.toString(); - annoBox.style.width = rect.width.toString(); - annoBox.style.height = rect.height.toString(); - // annoBox.style.transform = `scale(${1 / transform.scale}) translate(-${transform.translateX * transform.scale}px, -${transform.translateY * transform.scale}px)`; - this.textLayer.current.appendChild(annoBox); + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + this._textLayer.current.appendChild(annoBox); + this._currentAnnotations.push(annoBox); } } } @@ -403,9 +402,9 @@ class Page extends React.Component { return (
- +
-
+
); } -- cgit v1.2.3-70-g09d2 From 0a14ecf9b9fa5e15bc3e5373c8f042f9cd876c8a Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 6 Jun 2019 18:26:57 -0400 Subject: region annotation basics --- src/client/views/collections/CollectionPDFView.tsx | 3 + src/client/views/pdf/PDFAnnotationLayer.tsx | 24 ++++ src/client/views/pdf/PDFViewer.scss | 2 +- src/client/views/pdf/PDFViewer.tsx | 125 +++++++++++++++++---- 4 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 src/client/views/pdf/PDFAnnotationLayer.tsx (limited to 'src') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 5e51437a4..773e4ae60 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -13,6 +13,9 @@ import { Id } from "../../../new_fields/FieldSymbols"; @observer export class CollectionPDFView extends React.Component { + constructor(props: FieldViewProps) { + super(props); + } public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(CollectionPDFView, fieldKey); diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx new file mode 100644 index 000000000..e92dcacbf --- /dev/null +++ b/src/client/views/pdf/PDFAnnotationLayer.tsx @@ -0,0 +1,24 @@ +import React = require("react"); +import { observer } from "mobx-react"; + +interface IAnnotationProps { + +} + +@observer +export class PDFAnnotationLayer extends React.Component { + onPointerDown = (e: React.PointerEvent) => { + if (e.ctrlKey) { + console.log("annotating"); + e.stopPropagation(); + } + } + + render() { + return ( +
+ +
+ ) + } +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 484d9dc04..cb1aef410 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -27,5 +27,5 @@ .pdfViewer-annotationBox { position: absolute; background-color: red; - opacity: 0.5; + opacity: 0.1; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d5a0a7aa1..5c480090c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,6 +6,9 @@ import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; +import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; +import { TSMethodSignature } from "babel-types"; +import { checkPropTypes } from "prop-types"; interface IPDFViewerProps { url: string; @@ -268,15 +271,24 @@ class Page extends React.Component { @observable private _height: number = 0; @observable private _page: Opt; @observable private _currPage: number = this.props.page + 1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; private _canvas: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; private _textLayer: React.RefObject; + private _annotationLayer: React.RefObject; + private _marquee: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _marqueeing: boolean = false; constructor(props: IPageProps) { super(props); this._canvas = React.createRef(); this._textLayer = React.createRef(); + this._annotationLayer = React.createRef(); + this._marquee = React.createRef(); } componentDidMount() { @@ -342,9 +354,26 @@ class Page extends React.Component { } } + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 0) { - e.stopPropagation(); + let target: any = e.target; + if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + else { + e.stopPropagation(); + runInAction(() => { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } + }); + this._marqueeing = true; + if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; + } document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); if (!e.ctrlKey) { @@ -355,8 +384,22 @@ class Page extends React.Component { } } + @action onPointerMove = (e: PointerEvent) => { - if (e.button === 0) { + let target: any = e.target; + if (this._marqueeing) { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; + this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + console.log(this._marqueeWidth); + console.log(this._marqueeHeight); + } + e.stopPropagation(); + e.preventDefault(); + } + else if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); } } @@ -369,42 +412,76 @@ class Page extends React.Component { e.stopPropagation(); } + @action onPointerUp = (e: PointerEvent) => { - let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; - this._textLayer.current.appendChild(annoBox); - this._currentAnnotations.push(annoBox); + if (this._marqueeing) { + this._marqueeing = false; + if (this._marquee.current) { + let copy = document.createElement("div"); + copy.style.left = this._marquee.current.style.left; + copy.style.top = this._marquee.current.style.top; + copy.style.width = this._marquee.current.style.width; + copy.style.height = this._marquee.current.style.height; + copy.style.opacity = this._marquee.current.style.opacity; + copy.className = this._marquee.current.className; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(copy); + this._currentAnnotations.push(copy); + } + this._marquee.current.style.opacity = "0"; + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + // if selecting over a range of things + if (sel && sel.type === "Range") { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); + this._currentAnnotations.push(annoBox); + } } } + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } } } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); } + annotationPointerDown = (e: React.PointerEvent) => { + console.log("annotation"); + } + render() { return (
-
+
+
+
+
); } -- cgit v1.2.3-70-g09d2 From e96d0be82b19a4e108447ba5afc6cdc5deb07110 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 7 Jun 2019 15:28:25 -0400 Subject: starting annotation dragss --- src/client/util/DragManager.ts | 20 ++++++++ src/client/views/pdf/PDFViewer.tsx | 102 ++++++++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1e84a0db0..a6c3dfa7a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -153,6 +153,22 @@ export namespace DragManager { [id: string]: any; } + export class AnnotationDragData { + constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) { + this.dragDocument = dragDoc; + this.dropDocument = dropDoc; + this.annotationDocuments = annotationDocs; + this.xOffset = this.yOffset = 0; + } + dragDocument: Doc; + annotationDocuments: Doc[]; + dropDocument: Doc; + xOffset: number; + yOffset: number; + dropAction: dropActionType; + userDropAction: dropActionType; + } + export let StartDragFunctions: (() => void)[] = []; export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { @@ -166,6 +182,10 @@ export namespace DragManager { dragData.draggedDocuments)); } + export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) { + StartDrag(eles, dragData, downX, downY, options); + } + export class LinkDragData { constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) { this.linkSourceDocument = linkSourceDoc; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5c480090c..b0a48ac84 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,13 +2,19 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; import { TSMethodSignature } from "babel-types"; import { checkPropTypes } from "prop-types"; +import { DragManager } from "../../util/DragManager"; +import { Docs } from "../../documents/Documents"; +import { List } from "../../../new_fields/List"; +import { Cast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; +const Curly = require("./curly.png"); interface IPDFViewerProps { url: string; @@ -159,6 +165,7 @@ class Viewer extends React.Component { key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} + parent={this.props.parent} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(i => false); @@ -194,6 +201,7 @@ class Viewer extends React.Component { key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} + parent={this.props.parent} {...this.props} /> ); this._isPage[i] = true; @@ -262,6 +270,7 @@ interface IPageProps { numPages: number; page: number; pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; + parent: PDFBox; } @observer @@ -275,11 +284,13 @@ class Page extends React.Component { @observable private _marqueeY: number = 0; @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; + @observable private _rotate: string = ""; private _canvas: React.RefObject; private _textLayer: React.RefObject; private _annotationLayer: React.RefObject; private _marquee: React.RefObject; + private _curly: React.RefObject; private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; @@ -289,6 +300,7 @@ class Page extends React.Component { this._textLayer = React.createRef(); this._annotationLayer = React.createRef(); this._marquee = React.createRef(); + this._curly = React.createRef(); } componentDidMount() { @@ -338,7 +350,7 @@ class Page extends React.Component { this.props.pageLoaded(this._currPage, viewport); if (ctx) { // renders the page onto the canvas context - page.render({ canvasContext: ctx, viewport: viewport }) + page.render({ canvasContext: ctx, viewport: viewport }); // renders text onto the text container page.getTextContent().then((res: Pdfjs.TextContent) => { //@ts-ignore @@ -354,9 +366,56 @@ class Page extends React.Component { } } + makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { + let annoDoc = new Doc(); + annoDoc.x = anno.offsetLeft; + annoDoc.y = anno.offsetTop; + annoDoc.height = anno.offsetHeight; + annoDoc.width = anno.offsetWidth; + annoDoc.target = targetDoc; + annoDocs.push(annoDoc); + } + return annoDocs; + } + + startDrag = (e: PointerEvent) => { + console.log("start drag"); + e.preventDefault(); + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + let thisDoc = this.props.parent.Document; + let targetDoc = Docs.TextDocument(); + let annotationDocs = this.makeAnnotationDocuments(targetDoc); + targetDoc.annotations = new List(annotationDocs); + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + e.stopPropagation(); + } + + endDrag = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + e.stopPropagation(); + } + @action onPointerDown = (e: React.PointerEvent) => { - if (e.button === 0) { + if (e.shiftKey && e.button === 0) { + e.stopPropagation(); + + document.removeEventListener("pointermove", this.startDrag); + document.addEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + document.addEventListener("pointerup", this.endDrag); + } + else if (e.button === 0) { let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); @@ -374,7 +433,9 @@ class Page extends React.Component { this._marqueeing = true; if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; } + document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); if (!e.ctrlKey) { for (let anno of this._currentAnnotations) { @@ -393,8 +454,30 @@ class Page extends React.Component { let boundingRect = current.getBoundingClientRect(); this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; - console.log(this._marqueeWidth); - console.log(this._marqueeHeight); + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + this._marquee.current.style.background = "red"; + this._curly.current.style.opacity = "0"; + } + else { + this._marquee.current.style.background = "transparent"; + this._curly.current.style.opacity = "1"; + } + + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + this._rotate = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + this._rotate = "scale(2, 1)"; + } + else { + // diagonal + this._rotate = "rotate(45deg) scale(1.5, 1.5)"; + } + } } e.stopPropagation(); e.preventDefault(); @@ -472,6 +555,10 @@ class Page extends React.Component { console.log("annotation"); } + // imgVisible = () => { + // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } + // } + render() { return (
@@ -479,7 +566,10 @@ class Page extends React.Component {
-
+
+ +
-- cgit v1.2.3-70-g09d2 From 5d799d1a26fa12d666b11b80776151edcbbd67f8 Mon Sep 17 00:00:00 2001 From: loudonclear Date: Fri, 7 Jun 2019 18:14:39 -0400 Subject: some comments, switching computers --- src/client/util/DragManager.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 5 + src/client/views/pdf/PDFBox2.tsx | 28 -- src/client/views/pdf/PDFViewer.tsx | 327 +----------------- src/client/views/pdf/Page.tsx | 382 +++++++++++++++++++++ 5 files changed, 391 insertions(+), 353 deletions(-) delete mode 100644 src/client/views/pdf/PDFBox2.tsx create mode 100644 src/client/views/pdf/Page.tsx (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a6c3dfa7a..e92ed9b4a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -218,7 +218,7 @@ export namespace DragManager { let ys: number[] = []; const docs: Doc[] = - dragData instanceof DocumentDragData ? dragData.draggedDocuments : []; + dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, h = ele.offsetHeight; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index be37efd3d..1ced6a426 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -107,6 +107,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { e.stopPropagation(); return added; } + else if (de.data instanceof DragManager.AnnotationDragData) { + console.log("dropped!"); + console.log(de.data); + return this.props.addDocument(de.data.dropDocument); + } return false; } diff --git a/src/client/views/pdf/PDFBox2.tsx b/src/client/views/pdf/PDFBox2.tsx deleted file mode 100644 index 71825c260..000000000 --- a/src/client/views/pdf/PDFBox2.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React = require("react"); -import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { DocComponent } from "../DocComponent"; -import { makeInterface } from "../../../new_fields/Schema"; -import { positionSchema } from "../nodes/DocumentView"; -import { pageSchema } from "../nodes/ImageBox"; -import { PDFViewer } from "./PDFViewer"; -import { RouteStore } from "../../../server/RouteStore"; -import { InkingControl } from "../InkingControl"; -import { observer } from "mobx-react"; -import { trace } from "mobx"; - -type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; -const PdfDocument = makeInterface(positionSchema, pageSchema); - -@observer -export class PDFBox2 extends DocComponent(PdfDocument) { - public static LayoutString() { return FieldView.LayoutString(PDFBox2); } - - render() { - trace(); - const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"; - let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool); - return ( - - ) - } -} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b0a48ac84..0711ead23 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,19 +2,11 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, HeightSym, WidthSym, Field, DocListCast } from "../../../new_fields/Doc"; +import { Opt } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; -import { PDFAnnotationLayer } from "./PDFAnnotationLayer"; -import { TSMethodSignature } from "babel-types"; -import { checkPropTypes } from "prop-types"; -import { DragManager } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; -import { List } from "../../../new_fields/List"; -import { Cast } from "../../../new_fields/Types"; -import { emptyFunction } from "../../../Utils"; -const Curly = require("./curly.png"); +import Page from "./Page"; interface IPDFViewerProps { url: string; @@ -168,7 +160,7 @@ class Viewer extends React.Component { parent={this.props.parent} {...this.props} /> )); - let arr = Array.from(Array(numPages).keys()).map(i => false); + let arr = Array.from(Array(numPages).keys()).map(() => false); this._visibleElements.push(...divs); this._isPage.push(...arr); } @@ -263,316 +255,3 @@ class Viewer extends React.Component { ); } } - -interface IPageProps { - pdf: Opt; - name: string; - numPages: number; - page: number; - pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; - parent: PDFBox; -} - -@observer -class Page extends React.Component { - @observable private _state: string = "N/A"; - @observable private _width: number = 0; - @observable private _height: number = 0; - @observable private _page: Opt; - @observable private _currPage: number = this.props.page + 1; - @observable private _marqueeX: number = 0; - @observable private _marqueeY: number = 0; - @observable private _marqueeWidth: number = 0; - @observable private _marqueeHeight: number = 0; - @observable private _rotate: string = ""; - - private _canvas: React.RefObject; - private _textLayer: React.RefObject; - private _annotationLayer: React.RefObject; - private _marquee: React.RefObject; - private _curly: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; - private _marqueeing: boolean = false; - - constructor(props: IPageProps) { - super(props); - this._canvas = React.createRef(); - this._textLayer = React.createRef(); - this._annotationLayer = React.createRef(); - this._marquee = React.createRef(); - this._curly = React.createRef(); - } - - componentDidMount() { - if (this.props.pdf) { - this.update(this.props.pdf); - } - } - - componentDidUpdate() { - if (this.props.pdf) { - this.update(this.props.pdf); - } - } - - private update = (pdf: Pdfjs.PDFDocumentProxy) => { - if (pdf) { - this.loadPage(pdf); - } - else { - this._state = "loading"; - } - } - - private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { - if (this._state === "rendering" || this._page) return; - - pdf.getPage(this._currPage).then( - (page: Pdfjs.PDFPageProxy) => { - this._state = "rendering"; - this.renderPage(page); - } - ); - } - - @action - private renderPage = (page: Pdfjs.PDFPageProxy) => { - let scale = 1; - let viewport = page.getViewport(scale); - let canvas = this._canvas.current; - let textLayer = this._textLayer.current; - if (canvas && textLayer) { - let ctx = canvas.getContext("2d"); - canvas.width = viewport.width; - this._width = viewport.width; - canvas.height = viewport.height; - this._height = viewport.height; - this.props.pageLoaded(this._currPage, viewport); - if (ctx) { - // renders the page onto the canvas context - page.render({ canvasContext: ctx, viewport: viewport }); - // renders text onto the text container - page.getTextContent().then((res: Pdfjs.TextContent) => { - //@ts-ignore - Pdfjs.renderTextLayer({ - textContent: res, - container: textLayer, - viewport: viewport - }); - }); - - this._page = page; - } - } - } - - makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { - let annoDocs: Doc[] = []; - for (let anno of this._currentAnnotations) { - let annoDoc = new Doc(); - annoDoc.x = anno.offsetLeft; - annoDoc.y = anno.offsetTop; - annoDoc.height = anno.offsetHeight; - annoDoc.width = anno.offsetWidth; - annoDoc.target = targetDoc; - annoDocs.push(annoDoc); - } - return annoDocs; - } - - startDrag = (e: PointerEvent) => { - console.log("start drag"); - e.preventDefault(); - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - let thisDoc = this.props.parent.Document; - let targetDoc = Docs.TextDocument(); - let annotationDocs = this.makeAnnotationDocuments(targetDoc); - targetDoc.annotations = new List(annotationDocs); - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); - DragManager.StartAnnotationDrag(this._currentAnnotations, dragData, e.pageX, e.pageY, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); - e.stopPropagation(); - } - - endDrag = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - e.stopPropagation(); - } - - @action - onPointerDown = (e: React.PointerEvent) => { - if (e.shiftKey && e.button === 0) { - e.stopPropagation(); - - document.removeEventListener("pointermove", this.startDrag); - document.addEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - document.addEventListener("pointerup", this.endDrag); - } - else if (e.button === 0) { - let target: any = e.target; - if (target && target.parentElement === this._textLayer.current) { - e.stopPropagation(); - } - else { - e.stopPropagation(); - runInAction(() => { - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - } - }); - this._marqueeing = true; - if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; - } - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - if (!e.ctrlKey) { - for (let anno of this._currentAnnotations) { - anno.remove(); - } - } - } - } - - @action - onPointerMove = (e: PointerEvent) => { - let target: any = e.target; - if (this._marqueeing) { - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; - this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; - if (this._marquee.current && this._curly.current) { - if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { - this._marquee.current.style.background = "red"; - this._curly.current.style.opacity = "0"; - } - else { - this._marquee.current.style.background = "transparent"; - this._curly.current.style.opacity = "1"; - } - - let ratio = this._marqueeWidth / this._marqueeHeight; - if (ratio > 1.5) { - // vertical - this._rotate = "rotate(90deg) scale(1, 2)"; - } - else if (ratio < 0.5) { - // horizontal - this._rotate = "scale(2, 1)"; - } - else { - // diagonal - this._rotate = "rotate(45deg) scale(1.5, 1.5)"; - } - } - } - e.stopPropagation(); - e.preventDefault(); - } - else if (target && target.parentElement === this._textLayer.current) { - e.stopPropagation(); - } - } - - startAnnotation = (e: DragEvent) => { - console.log("drag starting"); - } - - pointerDownCancel = (e: PointerEvent) => { - e.stopPropagation(); - } - - @action - onPointerUp = (e: PointerEvent) => { - if (this._marqueeing) { - this._marqueeing = false; - if (this._marquee.current) { - let copy = document.createElement("div"); - copy.style.left = this._marquee.current.style.left; - copy.style.top = this._marquee.current.style.top; - copy.style.width = this._marquee.current.style.width; - copy.style.height = this._marquee.current.style.height; - copy.style.opacity = this._marquee.current.style.opacity; - copy.className = this._marquee.current.className; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(copy); - this._currentAnnotations.push(copy); - } - this._marquee.current.style.opacity = "0"; - } - - this._marqueeHeight = this._marqueeWidth = 0; - } - else { - let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; - if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); - this._currentAnnotations.push(annoBox); - } - } - } - if (sel.empty) { // Chrome - sel.empty(); - } else if (sel.removeAllRanges) { // Firefox - sel.removeAllRanges(); - } - } - } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - annotationPointerDown = (e: React.PointerEvent) => { - console.log("annotation"); - } - - // imgVisible = () => { - // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } - // } - - render() { - return ( -
-
- -
-
-
- -
-
-
-
- ); - } -} \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx new file mode 100644 index 000000000..7cd72b27f --- /dev/null +++ b/src/client/views/pdf/Page.tsx @@ -0,0 +1,382 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { observable, action, runInAction } from "mobx"; +import * as Pdfjs from "pdfjs-dist"; +import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc"; +import "./PDFViewer.scss"; +import "pdfjs-dist/web/pdf_viewer.css"; +import { PDFBox } from "../nodes/PDFBox"; +import { DragManager } from "../../util/DragManager"; +import { Docs } from "../../documents/Documents"; +import { List } from "../../../new_fields/List"; +import { emptyFunction } from "../../../Utils"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; + +interface IPageProps { + pdf: Opt; + name: string; + numPages: number; + page: number; + pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; + parent: PDFBox; +} + +@observer +export default class Page extends React.Component { + @observable private _state: string = "N/A"; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _page: Opt; + @observable private _currPage: number = this.props.page + 1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; + @observable private _rotate: string = ""; + @observable private _annotations: List = new List(); + + private _canvas: React.RefObject; + private _textLayer: React.RefObject; + private _annotationLayer: React.RefObject; + private _marquee: React.RefObject; + private _curly: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _marqueeing: boolean = false; + private _dragging: boolean = false; + + constructor(props: IPageProps) { + super(props); + this._canvas = React.createRef(); + this._textLayer = React.createRef(); + this._annotationLayer = React.createRef(); + this._marquee = React.createRef(); + this._curly = React.createRef(); + } + + componentDidMount() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + componentDidUpdate() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + private update = (pdf: Pdfjs.PDFDocumentProxy) => { + if (pdf) { + this.loadPage(pdf); + } + else { + this._state = "loading"; + } + } + + private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + if (this._state === "rendering" || this._page) return; + + pdf.getPage(this._currPage).then( + (page: Pdfjs.PDFPageProxy) => { + this._state = "rendering"; + this.renderPage(page); + } + ); + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy) => { + // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes + let scale = 2; + let viewport = page.getViewport(scale); + let canvas = this._canvas.current; + let textLayer = this._textLayer.current; + if (canvas && textLayer) { + let ctx = canvas.getContext("2d"); + canvas.width = viewport.width; + this._width = viewport.width; + canvas.height = viewport.height; + this._height = viewport.height; + this.props.pageLoaded(this._currPage, viewport); + if (ctx) { + // renders the page onto the canvas context + page.render({ canvasContext: ctx, viewport: viewport }); + // renders text onto the text container + page.getTextContent().then((res: Pdfjs.TextContent) => { + //@ts-ignore + Pdfjs.renderTextLayer({ + textContent: res, + container: textLayer, + viewport: viewport + }); + }); + + this._page = page; + } + } + } + + /** + * @param targetDoc: Document that annotations are linked to + * This method makes the list of current annotations into documents linked to + * the parameter passed in. + */ + makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { + let annoDoc = new Doc(); + annoDoc.x = anno.offsetLeft; + annoDoc.y = anno.offsetTop; + annoDoc.height = anno.offsetHeight; + annoDoc.width = anno.offsetWidth; + annoDoc.target = targetDoc; + annoDocs.push(annoDoc); + anno.remove(); + } + this._currentAnnotations = []; + return annoDocs; + } + + /** + * This is temporary for creating annotations from highlights. It will + * start a drag event and create or put the necessary info into the drag event. + */ + @action + startDrag = (e: PointerEvent) => { + // the first 5 lines is a hack to prevent text selection while dragging + e.preventDefault(); + e.stopPropagation(); + if (this._dragging) { + return; + } + this._dragging = true; + 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 = this.props.page; + // creates annotation documents for current highlights + let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + if (targetAnnotations) { + targetAnnotations.push(...annotationDocs); + targetDoc.annotations = targetAnnotations; + } + // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf + let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (thisAnnotations) { + thisAnnotations.push(...annotationDocs); + this.props.parent.Document.annotations = thisAnnotations; + let temp = new List(thisAnnotations); + temp.push(...this._annotations); + this._annotations = temp; + } + // create dragData and star tdrag + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + if (this._textLayer.current) { + DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + } + + // cleans up events and boolean + endDrag = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + this._dragging = false; + e.stopPropagation(); + } + + @action + onPointerDown = (e: React.PointerEvent) => { + // if alt+left click, drag and annotate + if (e.altKey && e.button === 0) { + e.stopPropagation(); + + document.removeEventListener("pointermove", this.startDrag); + document.addEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + document.addEventListener("pointerup", this.endDrag); + } + else if (e.button === 0) { + let target: any = e.target; + if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + else { + e.stopPropagation(); + // set marquee x and y positions to the spatially transformed position + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } + this._marqueeing = true; + if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + if (!e.ctrlKey) { + for (let anno of this._currentAnnotations) { + anno.remove(); + } + this._currentAnnotations = []; + } + } + } + + @action + onPointerMove = (e: PointerEvent) => { + let target: any = e.target; + if (this._marqueeing) { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; + this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + this._marquee.current.style.background = "red"; + this._curly.current.style.opacity = "0"; + } + else { + this._marquee.current.style.background = "transparent"; + this._curly.current.style.opacity = "1"; + } + + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + this._rotate = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + this._rotate = "scale(2, 1)"; + } + else { + // diagonal + this._rotate = "rotate(45deg) scale(1.5, 1.5)"; + } + } + } + e.stopPropagation(); + e.preventDefault(); + } + else if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + } + + startAnnotation = () => { + console.log("drag starting"); + } + + pointerDownCancel = (e: PointerEvent) => { + e.stopPropagation(); + } + + @action + onPointerUp = () => { + if (this._marqueeing) { + this._marqueeing = false; + if (this._marquee.current) { + let copy = document.createElement("div"); + copy.style.left = this._marquee.current.style.left; + copy.style.top = this._marquee.current.style.top; + copy.style.width = this._marquee.current.style.width; + copy.style.height = this._marquee.current.style.height; + copy.style.opacity = this._marquee.current.style.opacity; + copy.className = this._marquee.current.className; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(copy); + this._currentAnnotations.push(copy); + } + this._marquee.current.style.opacity = "0"; + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + // if selecting over a range of things + if (sel && sel.type === "Range") { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); + this._currentAnnotations.push(annoBox); + } + } + } + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { + let annoBox = document.createElement("div"); + if (anno) { + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = NumCast(anno.x).toString(); + annoBox.style.left = NumCast(anno.y).toString(); + annoBox.style.width = NumCast(anno.width).toString(); + annoBox.style.height = NumCast(anno.height).toString() + annoBox.onpointerdown = this.pointerDownCancel; + } + return annoBox; + } + + annotationPointerDown = () => { + console.log("annotation"); + } + + // imgVisible = () => { + // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } + // } + + render() { + let annotations = this._annotations; + return ( +
+
+ +
+
+
+ +
+ {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} +
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 92cdb8dae6162b1aa7e41f3392c670e7c5d377eb Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 7 Jun 2019 19:02:05 -0400 Subject: cleanup --- src/client/views/pdf/Page.tsx | 116 +++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7cd72b27f..940f31f41 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -211,19 +211,19 @@ export default class Page extends React.Component { else { e.stopPropagation(); // set marquee x and y positions to the spatially transformed position - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - } + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } this._marqueeing = true; if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; } - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.addEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); + document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { for (let anno of this._currentAnnotations) { anno.remove(); @@ -234,37 +234,20 @@ export default class Page extends React.Component { } @action - onPointerMove = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent) => { let target: any = e.target; if (this._marqueeing) { let current = this._textLayer.current; if (current) { + // transform positions and find the width and height to set the marquee to let boundingRect = current.getBoundingClientRect(); this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + let { background, opacity, transform: transform } = this.getCurlyTransform(); if (this._marquee.current && this._curly.current) { - if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { - this._marquee.current.style.background = "red"; - this._curly.current.style.opacity = "0"; - } - else { - this._marquee.current.style.background = "transparent"; - this._curly.current.style.opacity = "1"; - } - - let ratio = this._marqueeWidth / this._marqueeHeight; - if (ratio > 1.5) { - // vertical - this._rotate = "rotate(90deg) scale(1, 2)"; - } - else if (ratio < 0.5) { - // horizontal - this._rotate = "scale(2, 1)"; - } - else { - // diagonal - this._rotate = "rotate(45deg) scale(1.5, 1.5)"; - } + this._marquee.current.style.background = background; + this._curly.current.style.opacity = opacity; + this._rotate = transform; } } e.stopPropagation(); @@ -275,25 +258,62 @@ export default class Page extends React.Component { } } - startAnnotation = () => { - console.log("drag starting"); - } + getCurlyTransform = (): { background: string, opacity: string, transform: string } => { + let background = "", opacity = "", transform = ""; + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + background = "red"; + opacity = "0"; + } + else { + background = "transparent"; + opacity = "1"; + } - pointerDownCancel = (e: PointerEvent) => { - e.stopPropagation(); + // split up for simplicity, could be done in a nested ternary. please do not. -syip2 + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + transform = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + transform = "scale(2, 1)"; + } + else { + // diagonal + transform = "rotate(45deg) scale(1.5, 1.5)"; + } + } + return { background: background, opacity: opacity, transform: transform }; } @action - onPointerUp = () => { + onSelectEnd = () => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { let copy = document.createElement("div"); + // make a copy of the marquee copy.style.left = this._marquee.current.style.left; copy.style.top = this._marquee.current.style.top; copy.style.width = this._marquee.current.style.width; copy.style.height = this._marquee.current.style.height; - copy.style.opacity = this._marquee.current.style.opacity; + + // apply the appropriate background, opacity, and transform + let { background, opacity, transform } = this.getCurlyTransform(); + copy.style.background = background; + // if curly bracing, add a curly brace + if (opacity === "1" && this._curly.current) { + copy.style.opacity = opacity; + let img = this._curly.current.cloneNode(); + (img as any).style.opacity = opacity; + (img as any).style.transform = transform; + copy.appendChild(img); + } + else { + copy.style.opacity = this._marquee.current.style.opacity; + } copy.className = this._marquee.current.className; if (this._annotationLayer.current) { this._annotationLayer.current.append(copy); @@ -321,13 +341,12 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); this._currentAnnotations.push(annoBox); } } } + // clear selection if (sel.empty) { // Chrome sel.empty(); } else if (sel.removeAllRanges) { // Firefox @@ -335,8 +354,8 @@ export default class Page extends React.Component { } } } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); } renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { @@ -348,19 +367,10 @@ export default class Page extends React.Component { annoBox.style.left = NumCast(anno.y).toString(); annoBox.style.width = NumCast(anno.width).toString(); annoBox.style.height = NumCast(anno.height).toString() - annoBox.onpointerdown = this.pointerDownCancel; } return annoBox; } - annotationPointerDown = () => { - console.log("annotation"); - } - - // imgVisible = () => { - // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } - // } - render() { let annotations = this._annotations; return ( -- cgit v1.2.3-70-g09d2 From 81a502f81073e7fb15c75563cd557a6a2c5a31cd Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 10:58:00 -0400 Subject: annotation loading maybe idk not workign rn --- src/client/views/pdf/PDFViewer.tsx | 2 +- src/client/views/pdf/Page.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0711ead23..0a6886878 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -184,7 +184,7 @@ class Viewer extends React.Component { // render pages for any indices that don't already have pages (force rerender will make these render regardless) for (let i = startIndex; i <= endIndex; i++) { - if (!this._isPage[i] || forceRender) { + if (!this._isPage[i] || (this._isPage[i] && forceRender)) { this._visibleElements[i] = ( { targetAnnotations.push(...annotationDocs); targetDoc.annotations = targetAnnotations; } + else { + targetDoc.annotations = new List(annotationDocs); + } // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (thisAnnotations) { -- cgit v1.2.3-70-g09d2 From bcd6960fe91008bf31d364c48b5c8765eec28701 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 11:33:36 -0400 Subject: oops --- src/client/views/pdf/Page.tsx | 86 ++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 0d86f22c1..7125c820c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc"; +import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; @@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; import { Cast, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; +import { menuBar } from "prosemirror-menu"; interface IPageProps { pdf: Opt; @@ -34,7 +35,7 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: List = new List(); + @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -44,6 +45,7 @@ export default class Page extends React.Component { private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; + private _reactionDisposer?: IReactionDisposer; constructor(props: IPageProps) { super(props); @@ -54,19 +56,38 @@ export default class Page extends React.Component { this._curly = React.createRef(); } - componentDidMount() { + componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + + if (this.props.parent.Document) { + this._reactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + } + } + + componentWillUnmount = (): void => { + if (this._reactionDisposer) { + this._reactionDisposer(); } } - componentDidUpdate() { + componentDidUpdate = (): void => { if (this.props.pdf) { this.update(this.props.pdf); } } - private update = (pdf: Pdfjs.PDFDocumentProxy) => { + private update = (pdf: Pdfjs.PDFDocumentProxy): void => { if (pdf) { this.loadPage(pdf); } @@ -75,11 +96,11 @@ export default class Page extends React.Component { } } - private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + private loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => { if (this._state === "rendering" || this._page) return; pdf.getPage(this._currPage).then( - (page: Pdfjs.PDFPageProxy) => { + (page: Pdfjs.PDFPageProxy): void => { this._state = "rendering"; this.renderPage(page); } @@ -87,7 +108,18 @@ export default class Page extends React.Component { } @action - private renderPage = (page: Pdfjs.PDFPageProxy) => { + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes let scale = 2; let viewport = page.getViewport(scale); @@ -104,7 +136,7 @@ export default class Page extends React.Component { // renders the page onto the canvas context page.render({ canvasContext: ctx, viewport: viewport }); // renders text onto the text container - page.getTextContent().then((res: Pdfjs.TextContent) => { + page.getTextContent().then((res: Pdfjs.TextContent): void => { //@ts-ignore Pdfjs.renderTextLayer({ textContent: res, @@ -144,7 +176,7 @@ export default class Page extends React.Component { * start a drag event and create or put the necessary info into the drag event. */ @action - startDrag = (e: PointerEvent) => { + startDrag = (e: PointerEvent): void => { // the first 5 lines is a hack to prevent text selection while dragging e.preventDefault(); e.stopPropagation(); @@ -166,15 +198,6 @@ export default class Page extends React.Component { else { targetDoc.annotations = new List(annotationDocs); } - // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf - let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); - if (thisAnnotations) { - thisAnnotations.push(...annotationDocs); - this.props.parent.Document.annotations = thisAnnotations; - let temp = new List(thisAnnotations); - temp.push(...this._annotations); - this._annotations = temp; - } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); if (this._textLayer.current) { @@ -196,7 +219,7 @@ export default class Page extends React.Component { } @action - onPointerDown = (e: React.PointerEvent) => { + onPointerDown = (e: React.PointerEvent): void => { // if alt+left click, drag and annotate if (e.altKey && e.button === 0) { e.stopPropagation(); @@ -237,7 +260,7 @@ export default class Page extends React.Component { } @action - onSelectStart = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent): void => { let target: any = e.target; if (this._marqueeing) { let current = this._textLayer.current; @@ -292,7 +315,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = () => { + onSelectEnd = (): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -375,7 +398,6 @@ export default class Page extends React.Component { } render() { - let annotations = this._annotations; return (
@@ -386,10 +408,24 @@ export default class Page extends React.Component { style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
- {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} + {this._annotations.map(anno => )}
); } +} + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} +class RegionAnnotation extends React.Component { + render() { + return ( +
+ ); + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2ecd0c3486ec37d1061468cda12aa64e38d0d3d5 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 14:07:36 -0400 Subject: annotation improvements --- src/client/documents/Documents.ts | 8 +-- src/client/views/collections/CollectionSubView.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 64 ++++++++++++++-------- src/client/views/pdf/Page.tsx | 17 ++++-- 4 files changed, 57 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 973c90ff4..fa6d19a5e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,15 +69,15 @@ export interface DocumentOptions { const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { - export function MakeLink(source: Doc, target: Doc) { + export function MakeLink(source: Doc, target: Doc, title: string = "-link name-", description: string = "", tags: string = "Default") { let protoSrc = source.proto ? source.proto : source; let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; - linkDoc.proto!.linkDescription = ""; - linkDoc.proto!.linkTags = "Default"; + linkDoc.proto!.title = title; + linkDoc.proto!.linkDescription = description; + linkDoc.proto!.linkTags = tags; linkDoc.proto!.linkedTo = target; linkDoc.proto!.linkedToPage = target.curPage; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1ced6a426..d25d07c3d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -110,6 +110,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { else if (de.data instanceof DragManager.AnnotationDragData) { console.log("dropped!"); console.log(de.data); + // de.data.dropDocument.x = de.x; + // de.data.dropDocument.y = de.y; return this.props.addDocument(de.data.dropDocument); } return false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9d19df540..d8e197cbe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -81,31 +81,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { - if (de.data.droppedDocuments.length) { - let dragDoc = de.data.droppedDocuments[0]; - let zoom = NumCast(dragDoc.zoomBasis, 1); - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - let x = xp - de.data.xOffset / zoom; - let y = yp - de.data.yOffset / zoom; - let dropX = NumCast(de.data.droppedDocuments[0].x); - let dropY = NumCast(de.data.droppedDocuments[0].y); - de.data.droppedDocuments.forEach(d => { - d.x = x + NumCast(d.x) - dropX; - d.y = y + NumCast(d.y) - dropY; - if (!NumCast(d.width)) { - d.width = 300; - } - if (!NumCast(d.height)) { - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); - d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; - } - this.bringToFront(d); - }); - SelectionManager.ReselectAll(); + if (super.drop(e, de)) { + if (de.data instanceof DragManager.DocumentDragData) { + if (de.data.droppedDocuments.length) { + let dragDoc = de.data.droppedDocuments[0]; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.droppedDocuments[0].x); + let dropY = NumCast(de.data.droppedDocuments[0].y); + de.data.droppedDocuments.forEach(d => { + d.x = x + NumCast(d.x) - dropX; + d.y = y + NumCast(d.y) - dropY; + if (!NumCast(d.width)) { + d.width = 300; + } + if (!NumCast(d.height)) { + let nw = NumCast(d.nativeWidth); + let nh = NumCast(d.nativeHeight); + d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; + } + this.bringToFront(d); + }); + SelectionManager.ReselectAll(); + } + return true; + } + else if (de.data instanceof DragManager.AnnotationDragData) { + if (de.data.dropDocument) { + let dragDoc = de.data.dropDocument; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.dropDocument.x); + let dropY = NumCast(de.data.dropDocument.y); + dragDoc.x = x + NumCast(dragDoc.x) - dropX; + dragDoc.y = y + NumCast(dragDoc.y) - dropY; + this.bringToFront(dragDoc); + } } - return true; } return false; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7125c820c..5269f448b 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -7,10 +7,10 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import { DragManager } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; +import { Docs, DocUtils } from "../../documents/Documents"; import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; @@ -66,6 +66,7 @@ export default class Page extends React.Component { () => { let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { + annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); this.renderAnnotations(annotations, true); } }, @@ -163,8 +164,10 @@ export default class Page extends React.Component { annoDoc.y = anno.offsetTop; annoDoc.height = anno.offsetHeight; annoDoc.width = anno.offsetWidth; + annoDoc.page = this.props.page; annoDoc.target = targetDoc; annoDocs.push(annoDoc); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); } this._currentAnnotations = []; @@ -190,13 +193,14 @@ export default class Page extends React.Component { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + console.log(annotationDocs); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); - targetDoc.annotations = targetAnnotations; + this.props.parent.Document.annotations = new List(targetAnnotations); } else { - targetDoc.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List(annotationDocs); } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); @@ -422,10 +426,11 @@ interface IAnnotation { width: number; height: number; } + class RegionAnnotation extends React.Component { render() { return ( -
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 82a6a258422666fbe3db2a2cf1934f8673912b67 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 15:49:34 -0400 Subject: annotation updates blehh --- src/client/views/pdf/PDFViewer.scss | 6 +++ src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 44 +-------------------- 3 files changed, 81 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index cb1aef410..831e6add1 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -28,4 +28,10 @@ position: absolute; background-color: red; opacity: 0.1; +} + +.pdfViewer-annotationLayer { + position: absolute; + top: 0; + overflow: visible hidden; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0a6886878..1152587a4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,11 +2,12 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; +import { NumCast } from "../../../new_fields/Types"; interface IPDFViewerProps { url: string; @@ -65,9 +66,11 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; + @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; + private _annotationReactionDisposer?: IReactionDisposer; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -88,6 +91,19 @@ class Viewer extends React.Component { { fireImmediately: true } ); + if (this.props.parent.Document) { + this._annotationReactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + // On load, render pdf setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } @@ -96,6 +112,9 @@ class Viewer extends React.Component { if (this._reactionDisposer) { this._reactionDisposer(); } + if (this._annotationReactionDisposer) { + this._annotationReactionDisposer(); + } } @action @@ -135,6 +154,17 @@ class Viewer extends React.Component { } } + @action + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + /** * @param startIndex: where to start rendering pages * @param endIndex: where to end rendering pages @@ -158,6 +188,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -194,6 +225,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -247,11 +279,49 @@ class Viewer extends React.Component { } } + getPageHeight = (index: number): number => { + let counter = 0; + if (this.props.pdf && index < this.props.pdf.numPages) { + for (let i = 0; i < index; i++) { + if (this._pageSizes[i]) { + counter += this._pageSizes[i].height; + } + } + } + return counter; + } + render() { return ( -
- {this._visibleElements} +
+
+ {this._visibleElements} +
+
+
+ {this._annotations.map(anno => )} +
+
); } } + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} + +class RegionAnnotation extends React.Component { + onPointerDown = (e: React.PointerEvent) => { + console.log("clicked!"); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5269f448b..5738889c4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -21,6 +21,7 @@ interface IPageProps { page: number; pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; + renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; } @observer @@ -35,7 +36,6 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -60,19 +60,6 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); - if (this.props.parent.Document) { - this._reactionDisposer = reaction( - () => DocListCast(this.props.parent.Document.annotations), - () => { - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); - this.renderAnnotations(annotations, true); - } - }, - { fireImmediately: true } - ); - } } } @@ -108,17 +95,6 @@ export default class Page extends React.Component { ); } - @action - private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { - if (removeOldAnnotations) { - this._annotations = annotations; - } - else { - this._annotations.push(...annotations); - this._annotations = new Array(...this._annotations); - } - } - @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes @@ -407,30 +383,14 @@ export default class Page extends React.Component {
-
+
- {this._annotations.map(anno => )}
); } } - -interface IAnnotation { - x: number; - y: number; - width: number; - height: number; -} - -class RegionAnnotation extends React.Component { - render() { - return ( -
- ); - } -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 46b9a18bf278ab17845d1082a1152d16c07b1240 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 11:30:53 -0400 Subject: annotation navigationnn --- src/client/views/pdf/PDFViewer.tsx | 41 +++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1152587a4..092765324 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,11 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { DocUtils } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SelectionManager } from "../../util/SelectionManager"; interface IPDFViewerProps { url: string; @@ -297,9 +301,9 @@ class Viewer extends React.Component {
{this._visibleElements}
-
+
- {this._annotations.map(anno => )} + {this._annotations.map(anno => )}
@@ -307,21 +311,44 @@ class Viewer extends React.Component { } } -interface IAnnotation { +interface IAnnotationProps { x: number; y: number; width: number; height: number; + document: Doc; } -class RegionAnnotation extends React.Component { +class RegionAnnotation extends React.Component { + @observable private _backgroundColor: string = "red"; + + // private _reactionDisposer?: IReactionDisposer; + + constructor(props: IAnnotationProps) { + super(props); + + // this._reactionDisposer = reaction( + // () => { BoolCast(this.props.document.selected); }, + // () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; }, + // { fireImmediately: true } + // ) + } + onPointerDown = (e: React.PointerEvent) => { - console.log("clicked!"); + let targetDoc = Cast(this.props.document.target, Doc, null); + if (targetDoc) { + DocumentManager.Instance.jumpToDocument(targetDoc); + // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs); + // if (annotations && annotations.length) { + // annotations.forEach(anno => anno.selected = true); + // } + } } render() { return ( -
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 64f6a0d54656ded133af113746003d61eaa1d651 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 15:04:47 -0400 Subject: pin annotations base --- src/client/views/pdf/PDFViewer.scss | 6 ++++ src/client/views/pdf/PDFViewer.tsx | 72 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 22 ++++++++++-- 3 files changed, 96 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 831e6add1..57be04b93 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -34,4 +34,10 @@ position: absolute; top: 0; overflow: visible hidden; +} + +.pdfViewer-pinAnnotation { + background-color: red; + position: absolute; + border-radius: 100%; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 092765324..bc773ebef 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,9 +9,11 @@ import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; -import { DocUtils } from "../../documents/Documents"; +import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SelectionManager } from "../../util/SelectionManager"; +import { List } from "../../../new_fields/List"; +import { DocumentContentsView } from "../nodes/DocumentContentsView"; interface IPDFViewerProps { url: string; @@ -56,6 +58,8 @@ interface IViewerProps { url: string; } +const PinRadius = 25; + /** * Handles rendering and virtualization of the pdf */ @@ -193,6 +197,7 @@ class Viewer extends React.Component { pageLoaded={this.pageLoaded} parent={this.props.parent} renderAnnotations={this.renderAnnotations} + makePin={this.createPinAnnotation} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -229,6 +234,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} {...this.props} /> ); @@ -242,6 +248,33 @@ class Viewer extends React.Component { return; } + createPinAnnotation = (x: number, y: number): void => { + let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + + let pinAnno = new Doc(); + pinAnno.x = x; + pinAnno.y = y; + pinAnno.width = pinAnno.height = PinRadius; + pinAnno.page = this.getIndex(y); + pinAnno.target = targetDoc; + pinAnno.type = AnnotationTypes.Pin; + // this._annotations.push(pinAnno); + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + annotations.push(pinAnno); + this.props.parent.Document.annotations = new List(annotations); + } + else { + this.props.parent.Document.annotations = new List([pinAnno]); + } + // let pinAnno = document.createElement("div"); + // pinAnno.className = "pdfViewer-pinAnnotation"; + // pinAnno.style.top = (y - (radius / 2)).toString(); + // pinAnno.style.left = (x - (radius / 2)).toString(); + // pinAnno.style.height = pinAnno.style.width = radius.toString(); + // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno); + } + // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { if (this._loaded) { @@ -295,6 +328,18 @@ class Viewer extends React.Component { return counter; } + renderAnnotation = (anno: Doc): JSX.Element => { + let type = NumCast(anno.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + } + render() { return (
@@ -303,7 +348,7 @@ class Viewer extends React.Component {
- {this._annotations.map(anno => )} + {this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -311,6 +356,10 @@ class Viewer extends React.Component { } } +export enum AnnotationTypes { + Region, Pin +} + interface IAnnotationProps { x: number; y: number; @@ -319,6 +368,25 @@ interface IAnnotationProps { document: Doc; } +class PinAnnotation extends React.Component { + @observable private _backgroundColor: string = "red"; + + pointerDown = (e: React.PointerEvent) => { + + } + + render() { + let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); + return ( +
+ {/* */} +
+ ); + } +} + class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5738889c4..fe7369aeb 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,6 +13,7 @@ import { emptyFunction } from "../../../Utils"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; +import { AnnotationTypes } from "./PDFViewer"; interface IPageProps { pdf: Opt; @@ -22,6 +23,7 @@ interface IPageProps { pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; + makePin: (x: number, y: number) => void; } @observer @@ -142,6 +144,7 @@ export default class Page extends React.Component { annoDoc.width = anno.offsetWidth; annoDoc.page = this.props.page; annoDoc.target = targetDoc; + annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); @@ -169,7 +172,6 @@ export default class Page extends React.Component { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - console.log(annotationDocs); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); @@ -364,6 +366,22 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); } + doubleClick = (e: React.MouseEvent) => { + let target: any = e.target; + // if double clicking text + if (target && target.parentElement === this._textLayer.current) { + // do something to select the paragraph ideally + } + + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + this.props.makePin(x, y); + } + } + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { let annoBox = document.createElement("div"); if (anno) { @@ -379,7 +397,7 @@ export default class Page extends React.Component { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From b96281d18a9c4ca0ea7f8360d7f69d12c325fada Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 19:02:00 -0400 Subject: pin annotations --- src/client/views/nodes/PDFBox.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 136 +++++++++++++++++++++++++++++-------- src/client/views/pdf/Page.tsx | 4 +- 3 files changed, 110 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index de75c67cf..5b118185b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -50,6 +50,7 @@ export class PDFBox extends DocComponent(PdfDocumen trace(); // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); + console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
{ @action componentDidMount() { const pdfUrl = this.props.url; + console.log("pdf starting to load") let promise = Pdfjs.getDocument(pdfUrl).promise; promise.then((pdf: Pdfjs.PDFDocumentProxy) => { - runInAction(() => this._pdf = pdf); + runInAction(() => { + console.log("pdf url received"); + this._pdf = pdf; + }); }); } @@ -77,8 +85,16 @@ class Viewer extends React.Component { @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; + private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; + private _pagesLoaded: number = 0; + + constructor(props: IViewerProps) { + super(props); + + this._annotationLayer = React.createRef(); + } componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -112,8 +128,9 @@ class Viewer extends React.Component { ); } - // On load, render pdf - setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); + setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + }, 1000); } componentWillUnmount = () => { @@ -150,7 +167,7 @@ class Viewer extends React.Component { } @computed get endIndex(): number { - let width = this._pageSizes.map(i => i.width); + let width = this._pageSizes.map(i => i ? i.width : 0); return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); } @@ -185,6 +202,10 @@ class Viewer extends React.Component { return; } + if (this._pageSizes.length !== numPages) { + this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 })); + } + // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( @@ -248,14 +269,14 @@ class Viewer extends React.Component { return; } - createPinAnnotation = (x: number, y: number): void => { - let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + 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; pinAnno.width = pinAnno.height = PinRadius; - pinAnno.page = this.getIndex(y); + pinAnno.page = page; pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); @@ -301,18 +322,18 @@ class Viewer extends React.Component { } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; this.props.loaded(page.width, page.height); - if (index > this._pageSizes.length) { - this._pageSizes.push({ width: page.width, height: page.height }); - } - else { - this._pageSizes[index - 1] = { width: page.width, height: page.height }; - } - if (index === numPages) { + this._pageSizes[index - 1] = { width: page.width, height: page.height }; + this._pagesLoaded++; + if (this._pagesLoaded === numPages) { this._loaded = true; let divs = Array.from(Array(numPages).keys()).map(i => (
)); this._visibleElements = new Array(...divs); + // On load, render pdf + // setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + // }, 1000); } } @@ -332,21 +353,38 @@ class Viewer extends React.Component { let type = NumCast(anno.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } } + onDrop = (e: React.DragEvent) => { + console.log("Dropped!"); + } + + // ScreenToLocalTransform = (): Transform => { + // if (this._annotationLayer.current) { + // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); + // let x = boundingRect.left; + // let y = boundingRect.top; + // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; + // let t = new Transform(x, y, scale); + // return t; + // } + // return Transform.Identity(); + // } + render() { + trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -365,25 +403,65 @@ interface IAnnotationProps { y: number; width: number; height: number; + parent: Viewer; document: Doc; } +@observer class PinAnnotation extends React.Component { - @observable private _backgroundColor: string = "red"; + @observable private _backgroundColor: string = "green"; + @observable private _display: string = "initial"; - pointerDown = (e: React.PointerEvent) => { + private _selected: boolean = true; + @action + pointerDown = (e: React.PointerEvent) => { + if (this._selected) { + this._backgroundColor = "red"; + this._display = "none"; + this._selected = false; + } + else { + this._backgroundColor = "green"; + this._display = "initial"; + this._selected = true; + } + e.preventDefault(); + e.stopPropagation(); } render() { - let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); - return ( -
- {/* */} -
- ); + let targetDoc = Cast(this.props.document.target, Doc); + if (targetDoc instanceof Doc) { + return ( +
+
+ 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} + /> +
+
+ ); + } + return null; } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index fe7369aeb..73a7a93a0 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -23,7 +23,7 @@ interface IPageProps { pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; - makePin: (x: number, y: number) => void; + makePin: (x: number, y: number, page: number) => void; } @observer @@ -378,7 +378,7 @@ export default class Page extends React.Component { let boundingRect = current.getBoundingClientRect(); let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - this.props.makePin(x, y); + this.props.makePin(x, y, this.props.page); } } -- cgit v1.2.3-70-g09d2 From 9ec4a529dc886acca8f147cfe913e60f938f3bda Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 14:35:24 -0400 Subject: reverse annotating --- src/client/util/DragManager.ts | 6 +- src/client/views/pdf/PDFViewer.tsx | 112 ++++++++++++++++++++++++++++++------- src/client/views/pdf/Page.tsx | 27 ++++++--- 3 files changed, 116 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e92ed9b4a..2ffb77e44 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -154,14 +154,14 @@ export namespace DragManager { } export class AnnotationDragData { - constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) { + constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) { this.dragDocument = dragDoc; this.dropDocument = dropDoc; - this.annotationDocuments = annotationDocs; + this.annotationDocument = annotationDoc; this.xOffset = this.yOffset = 0; } dragDocument: Doc; - annotationDocuments: Doc[]; + annotationDocument: Doc; dropDocument: Doc; xOffset: number; yOffset: number; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6823f640e..bfd0e036f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,7 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; -import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; +import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; @@ -18,6 +18,8 @@ import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocum import { Transform } from "../../util/Transform"; import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; import { DocumentView } from "../nodes/DocumentView"; +import { DragManager } from "../../util/DragManager"; +import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; @@ -83,12 +85,15 @@ class Viewer extends React.Component { @observable private _loaded: boolean = false; @observable private _pdf: Opt; @observable private _annotations: Doc[] = []; + @observable private _pointerEvents: "all" | "none" = "all"; + @observable private _savedAnnotations: Dictionary = new Dictionary(); private _pageBuffer: number = 1; private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _pagesLoaded: number = 0; + private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: IViewerProps) { super(props); @@ -96,6 +101,7 @@ class Viewer extends React.Component { this._annotationLayer = React.createRef(); } + @action componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); // reaction for when document gets (de)selected @@ -105,10 +111,12 @@ class Viewer extends React.Component { // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); + this._pointerEvents = "all"; } // if selected, render pdf else if (!wasSelected && this.props.parent.props.isSelected()) { this.renderPages(this.startIndex, this.endIndex, true); + this._pointerEvents = "none"; } wasSelected = this.props.parent.props.isSelected(); }, @@ -133,6 +141,57 @@ class Viewer extends React.Component { }, 1000); } + private mainCont = (div: HTMLDivElement | null) => { + if (this._dropDisposer) { + this._dropDisposer(); + } + if (div) { + this._dropDisposer = DragManager.MakeDropTarget(div, { + handlers: { drop: this.drop.bind(this) } + }); + } + } + + makeAnnotationDocuments = (sourceDoc: Doc): Doc => { + let annoDocs: Doc[] = []; + this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { + for (let anno of value) { + let annoDoc = new Doc(); + 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); + if (anno.style.width) annoDoc.width = parseInt(anno.style.width); + annoDoc.page = key; + annoDoc.target = sourceDoc; + annoDoc.type = AnnotationTypes.Region; + annoDocs.push(annoDoc); + anno.remove(); + } + }); + + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + this._savedAnnotations.clear(); + return annoDoc; + } + + drop = async (e: Event, de: DragManager.DropEvent) => { + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc = de.data.linkSourceDocument; + let destDoc = this.makeAnnotationDocuments(sourceDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(destDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([destDoc]); + } + } + e.stopPropagation(); + } + componentWillUnmount = () => { if (this._reactionDisposer) { this._reactionDisposer(); @@ -219,6 +278,8 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -257,6 +318,8 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -269,6 +332,15 @@ class Viewer extends React.Component { return; } + @action + receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { + this._savedAnnotations.setValue(page, annotations); + } + + sendAnnotations = (page: number): HTMLDivElement[] | undefined => { + 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" }); @@ -280,13 +352,15 @@ class Viewer extends React.Component { pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); + let annoDoc = new Doc(); + annoDoc.annotations = new List([pinAnno]); let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { - annotations.push(pinAnno); + annotations.push(annoDoc); this.props.parent.Document.annotations = new List(annotations); } else { - this.props.parent.Document.annotations = new List([pinAnno]); + this.props.parent.Document.annotations = new List([annoDoc]); } // let pinAnno = document.createElement("div"); // pinAnno.className = "pdfViewer-pinAnnotation"; @@ -349,20 +423,20 @@ class Viewer extends React.Component { return counter; } - renderAnnotation = (anno: Doc): JSX.Element => { - let type = NumCast(anno.type); - switch (type) { - case AnnotationTypes.Pin: - return ; - case AnnotationTypes.Region: - return ; - default: - return
; - } - } - - onDrop = (e: React.DragEvent) => { - console.log("Dropped!"); + renderAnnotation = (anno: Doc): JSX.Element[] => { + let annotationDocs = DocListCast(anno.annotations); + let res = annotationDocs.map(a => { + let type = NumCast(a.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + }); + return res; } // ScreenToLocalTransform = (): Transform => { @@ -380,11 +454,11 @@ class Viewer extends React.Component { render() { trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 73a7a93a0..908804605 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -24,6 +24,8 @@ interface IPageProps { parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; makePin: (x: number, y: number, page: number) => void; + sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; + receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; } @observer @@ -61,11 +63,19 @@ export default class Page extends React.Component { componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + } + let received = this.props.receiveAnnotations(this.props.page); + this._currentAnnotations = received ? received : []; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(...this._currentAnnotations); } } componentWillUnmount = (): void => { + console.log(this._currentAnnotations); + this.props.sendAnnotations(this._currentAnnotations, this.props.page); + if (this._reactionDisposer) { this._reactionDisposer(); } @@ -134,8 +144,9 @@ export default class Page extends React.Component { * This method makes the list of current annotations into documents linked to * the parameter passed in. */ - makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + makeAnnotationDocuments = (targetDoc: Doc): Doc => { let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { let annoDoc = new Doc(); annoDoc.x = anno.offsetLeft; @@ -146,11 +157,13 @@ export default class Page extends React.Component { annoDoc.target = targetDoc; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); - DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); } + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); this._currentAnnotations = []; - return annoDocs; + return annoDoc; } /** @@ -171,17 +184,17 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { - targetAnnotations.push(...annotationDocs); + targetAnnotations.push(annotationDoc); this.props.parent.Document.annotations = new List(targetAnnotations); } else { - this.props.parent.Document.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List([annotationDoc]); } // create dragData and star tdrag - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { handlers: { -- cgit v1.2.3-70-g09d2 From 2cec2be7f16cf8ef656def60659e5555b75f2ce7 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 15:09:11 -0400 Subject: oops --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bfd0e036f..deacdd623 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -171,7 +171,7 @@ class Viewer extends React.Component { let annoDoc = new Doc(); annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); this._savedAnnotations.clear(); return annoDoc; } -- cgit v1.2.3-70-g09d2 From 0f85b1b7a6198013269c19a36dc8bcf14d2a4952 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 16:02:25 -0400 Subject: Refactored some code --- src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++--------------------- src/client/views/pdf/Page.tsx | 67 +++------------------------------ 2 files changed, 39 insertions(+), 104 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index deacdd623..e54dfea6f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -278,7 +278,9 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> )); @@ -318,7 +320,9 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> ); @@ -334,7 +338,13 @@ class Viewer extends React.Component { @action receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { - this._savedAnnotations.setValue(page, annotations); + if (page === -1) { + this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations)); + } + else { + this._savedAnnotations.setValue(page, annotations); + } } sendAnnotations = (page: number): HTMLDivElement[] | undefined => { @@ -346,7 +356,7 @@ class Viewer extends React.Component { let pinAnno = new Doc(); pinAnno.x = x; - pinAnno.y = y; + pinAnno.y = y + this.getPageHeight(page); pinAnno.width = pinAnno.height = PinRadius; pinAnno.page = page; pinAnno.target = targetDoc; @@ -362,12 +372,6 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([annoDoc]); } - // let pinAnno = document.createElement("div"); - // pinAnno.className = "pdfViewer-pinAnnotation"; - // pinAnno.style.top = (y - (radius / 2)).toString(); - // pinAnno.style.left = (x - (radius / 2)).toString(); - // pinAnno.style.height = pinAnno.style.width = radius.toString(); - // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno); } // get the page index that the vertical offset passed in is on @@ -404,10 +408,7 @@ class Viewer extends React.Component {
)); this._visibleElements = new Array(...divs); - // On load, render pdf - // setTimeout(() => { this.renderPages(this.startIndex, this.endIndex, true); - // }, 1000); } } @@ -423,15 +424,32 @@ class Viewer extends React.Component { return counter; } + createAnnotation = (div: HTMLDivElement, page: number) => { + if (this._annotationLayer.current) { + if (div.style.top) { + div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString(); + } + this._annotationLayer.current.append(div); + let savedPage = this._savedAnnotations.getValue(page); + if (savedPage) { + savedPage.push(div); + this._savedAnnotations.setValue(page, savedPage); + } + else { + this._savedAnnotations.setValue(page, [div]); + } + } + } + renderAnnotation = (anno: Doc): JSX.Element[] => { let annotationDocs = DocListCast(anno.annotations); let res = annotationDocs.map(a => { let type = NumCast(a.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } @@ -439,18 +457,6 @@ class Viewer extends React.Component { return res; } - // ScreenToLocalTransform = (): Transform => { - // if (this._annotationLayer.current) { - // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); - // let x = boundingRect.left; - // let y = boundingRect.top; - // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; - // let t = new Transform(x, y, scale); - // return t; - // } - // return Transform.Identity(); - // } - render() { trace(); return ( @@ -458,8 +464,8 @@ class Viewer extends React.Component {
{this._visibleElements}
-
-
+
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -542,26 +548,10 @@ class PinAnnotation extends React.Component { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - // private _reactionDisposer?: IReactionDisposer; - - constructor(props: IAnnotationProps) { - super(props); - - // this._reactionDisposer = reaction( - // () => { BoolCast(this.props.document.selected); }, - // () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; }, - // { fireImmediately: true } - // ) - } - onPointerDown = (e: React.PointerEvent) => { let targetDoc = Cast(this.props.document.target, Doc, null); if (targetDoc) { DocumentManager.Instance.jumpToDocument(targetDoc); - // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs); - // if (annotations && annotations.length) { - // annotations.forEach(anno => anno.selected = true); - // } } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 998ac25fd..9e3bf4954 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -26,6 +26,8 @@ interface IPageProps { makePin: (x: number, y: number, page: number) => void; sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; + createAnnotation: (div: HTMLDivElement, page: number) => void; + makeAnnotationDocuments: (doc: Doc) => Doc; } @observer @@ -46,7 +48,6 @@ export default class Page extends React.Component { private _annotationLayer: React.RefObject; private _marquee: React.RefObject; private _curly: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; private _reactionDisposer?: IReactionDisposer; @@ -64,18 +65,9 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); } - - let received = this.props.receiveAnnotations(this.props.page); - this._currentAnnotations = received ? received : []; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(...this._currentAnnotations); - } } componentWillUnmount = (): void => { - console.log(this._currentAnnotations); - this.props.sendAnnotations(this._currentAnnotations, this.props.page); - if (this._reactionDisposer) { this._reactionDisposer(); } @@ -139,33 +131,6 @@ export default class Page extends React.Component { } } - /** - * @param targetDoc: Document that annotations are linked to - * This method makes the list of current annotations into documents linked to - * the parameter passed in. - */ - makeAnnotationDocuments = (targetDoc: Doc): Doc => { - let annoDocs: Doc[] = []; - - for (let anno of this._currentAnnotations) { - let annoDoc = new Doc(); - annoDoc.x = anno.offsetLeft; - annoDoc.y = anno.offsetTop; - annoDoc.height = anno.offsetHeight; - annoDoc.width = anno.offsetWidth; - annoDoc.page = this.props.page; - annoDoc.target = targetDoc; - annoDoc.type = AnnotationTypes.Region; - annoDocs.push(annoDoc); - anno.remove(); - } - let annoDoc = new Doc(); - annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(annoDoc, targetDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); - this._currentAnnotations = []; - return annoDoc; - } - /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -184,7 +149,7 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(annotationDoc); @@ -246,10 +211,7 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { - for (let anno of this._currentAnnotations) { - anno.remove(); - } - this._currentAnnotations = []; + this.props.sendAnnotations([], -1); } } } @@ -336,10 +298,7 @@ export default class Page extends React.Component { copy.style.opacity = this._marquee.current.style.opacity; } copy.className = this._marquee.current.className; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(copy); - this._currentAnnotations.push(copy); - } + this.props.createAnnotation(copy, this.props.page); this._marquee.current.style.opacity = "0"; } @@ -362,8 +321,7 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); - this._currentAnnotations.push(annoBox); + this.props.createAnnotation(annoBox, this.props.page); } } } @@ -395,19 +353,6 @@ export default class Page extends React.Component { } } - renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { - let annoBox = document.createElement("div"); - if (anno) { - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = NumCast(anno.x).toString(); - annoBox.style.left = NumCast(anno.y).toString(); - annoBox.style.width = NumCast(anno.width).toString(); - annoBox.style.height = NumCast(anno.height).toString() - } - return annoBox; - } - render() { return (
-- cgit v1.2.3-70-g09d2 From eaa66ece6340534ad09cf83134b344ef43816cd9 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 18:30:53 -0400 Subject: deleting pins and better pin saving --- src/client/views/nodes/PDFBox.scss | 23 +++++++++++---- src/client/views/nodes/PDFBox.tsx | 3 ++ src/client/views/pdf/PDFViewer.tsx | 57 ++++++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 449408a61..f4d455be7 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -2,39 +2,52 @@ transform-origin: left top; position: absolute; top: 0; - left:0; + left: 0; } + .react-pdf__Page__textContent span { user-select: text; } + .react-pdf__Document { position: absolute; } + .pdfBox-buttonTray { - position:absolute; + position: absolute; top: 0; - left:0; + left: 0; z-index: 25; pointer-events: all; } + .pdfBox-thumbnail { position: absolute; width: 100%; } + .pdfButton { pointer-events: all; width: 100px; - height:100px; + height: 100px; } + .pdfBox-cont { - pointer-events: none ; + pointer-events: none; + display: flex; + flex-direction: row; + span { pointer-events: none !important; } } + .pdfBox-cont-interactive { pointer-events: all; + display: flex; + flex-direction: row; } + .pdfBox-contentContainer { position: absolute; transform-origin: left top; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5b118185b..4214a6777 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -19,6 +19,7 @@ import { makeInterface } from "../../../new_fields/Schema"; import { PDFViewer } from "../pdf/PDFViewer"; import { PdfField } from "../../../new_fields/URLField"; import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -43,6 +44,7 @@ export class PDFBox extends DocComponent(PdfDocumen onScroll = (e: React.UIEvent) => { if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; + // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); } } @@ -57,6 +59,7 @@ export class PDFBox extends DocComponent(PdfDocumen style={{ overflowY: "scroll", overflowX: "hidden", height: `${NumCast(this.props.Document.nativeHeight ? this.props.Document.nativeHeight : 300)}px` }} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> + {/*
*/}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index e54dfea6f..fe442c906 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,7 +152,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocuments = (sourceDoc: Doc): Doc => { + makeAnnotationDocument = (sourceDoc: Doc): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -179,7 +179,7 @@ class Viewer extends React.Component { drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocuments(sourceDoc); + let destDoc = this.makeAnnotationDocument(sourceDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(destDoc); @@ -280,7 +280,7 @@ class Viewer extends React.Component { makePin={this.createPinAnnotation} createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocuments} + makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> )); @@ -322,7 +322,7 @@ class Viewer extends React.Component { renderAnnotations={this.renderAnnotations} createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocuments} + makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> ); @@ -492,29 +492,70 @@ class PinAnnotation extends React.Component { @observable private _backgroundColor: string = "green"; @observable private _display: string = "initial"; - private _selected: boolean = true; + private _mainCont: React.RefObject; + + constructor(props: IAnnotationProps) { + super(props); + this._mainCont = React.createRef(); + } + + componentDidMount = () => { + let selected = this.props.document.selected; + if (selected && BoolCast(selected)) { + runInAction(() => { + this._backgroundColor = "green"; + this._display = "initial"; + }) + } + else { + runInAction(() => { + this._backgroundColor = "red"; + this._display = "none"; + }) + } + } @action pointerDown = (e: React.PointerEvent) => { - if (this._selected) { + let selected = this.props.document.selected; + if (selected && BoolCast(selected)) { this._backgroundColor = "red"; this._display = "none"; - this._selected = false; + this.props.document.selected = false; } else { this._backgroundColor = "green"; this._display = "initial"; - this._selected = true; + 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(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 (
Date: Thu, 13 Jun 2019 22:00:33 -0400 Subject: added collection back --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionPDFView.tsx | 32 +++++++++++++++++++--- src/client/views/nodes/PDFBox.tsx | 13 ++++++++- src/client/views/pdf/PDFViewer.scss | 1 - src/client/views/pdf/PDFViewer.tsx | 30 ++++++++++++++------ src/client/views/pdf/Page.tsx | 2 +- 6 files changed, 64 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dfbe2e136..91d3707f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -170,7 +170,7 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(), + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 62e8adbec..4af89d780 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { ContextMenu } from "../ContextMenu"; import "./CollectionPDFView.scss"; @@ -9,12 +9,36 @@ import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from ". import { emptyFunction } from "../../../Utils"; import { NumCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; +import { HeightSym, WidthSym } from "../../../new_fields/Doc"; @observer export class CollectionPDFView extends React.Component { + private _reactionDisposer?: IReactionDisposer; + private _buttonTray: React.RefObject; + constructor(props: FieldViewProps) { super(props); + + this._buttonTray = React.createRef(); + } + + componentDidMount() { + this._reactionDisposer = reaction( + () => NumCast(this.props.Document.scrollY), + () => { + // let transform = this.props.ScreenToLocalTransform(); + if (this._buttonTray.current) { + // console.log(this._buttonTray.current.offsetHeight); + // console.log(NumCast(this.props.Document.scrollY)); + let scale = this.nativeWidth() / this.props.Document[WidthSym](); + this.props.Document.panY = NumCast(this.props.Document.scrollY); + // console.log(scale); + } + // console.log(this.props.Document[HeightSym]()); + }, + { fireImmediately: true } + ) } public static LayoutString(fieldKey: string = "data") { @@ -52,12 +76,12 @@ export class CollectionPDFView extends React.Component { private get uIButtons() { let ratio = (this.curPage - 1) / this.numPages * 100; return ( -
+
-
+ {/*
-
+
*/}
); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4214a6777..acb430deb 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -36,6 +36,10 @@ export class PDFBox extends DocComponent(PdfDocumen let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.nativeWidth = nw; doc.nativeHeight = nh; + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.pdfHeight = nh; + } doc.height = nh * (doc[WidthSym]() / nw); } } @@ -45,6 +49,10 @@ export class PDFBox extends DocComponent(PdfDocumen if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.scrollY = this._scrollY; + } } } @@ -56,7 +64,10 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 57be04b93..a73df2d58 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -33,7 +33,6 @@ .pdfViewer-annotationLayer { position: absolute; top: 0; - overflow: visible hidden; } .pdfViewer-pinAnnotation { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index fe442c906..144fca9e0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -188,8 +188,8 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([destDoc]); } + e.stopPropagation(); } - e.stopPropagation(); } componentWillUnmount = () => { @@ -465,7 +465,7 @@ class Viewer extends React.Component { {this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -501,17 +501,31 @@ class PinAnnotation extends React.Component { componentDidMount = () => { let selected = this.props.document.selected; - if (selected && BoolCast(selected)) { + if (!BoolCast(selected)) { runInAction(() => { - this._backgroundColor = "green"; - this._display = "initial"; - }) + 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"; - }) + }); } } @@ -572,7 +586,7 @@ class PinAnnotation extends React.Component { PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} focus={emptyFunction} - selectOnLoad={false} + selectOnLoad={true} parentActive={this.props.parent.props.parent.props.active} whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} bringToFront={emptyFunction} diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 9e3bf4954..1c305caa3 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -257,7 +257,7 @@ export default class Page extends React.Component { let ratio = this._marqueeWidth / this._marqueeHeight; if (ratio > 1.5) { // vertical - transform = "rotate(90deg) scale(1, 2)"; + transform = "rotate(90deg) scale(1, 5)"; } else if (ratio < 0.5) { // horizontal -- cgit v1.2.3-70-g09d2 From 6a2cb71af332d4c782c1678750ba955757eaab45 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 11:56:36 -0400 Subject: fixes --- src/client/views/nodes/PDFBox.scss | 6 ++++++ src/client/views/pdf/PDFViewer.scss | 2 ++ src/client/views/pdf/PDFViewer.tsx | 10 +++++----- src/client/views/pdf/Page.tsx | 3 +-- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index f4d455be7..bb1f534c6 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -41,6 +41,12 @@ pointer-events: none !important; } } +.textlayer { + span { + pointer-events: all !important; + user-select: text; + } +} .pdfBox-cont-interactive { pointer-events: all; diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a73df2d58..53c33ce0b 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -35,6 +35,8 @@ top: 0; } + + .pdfViewer-pinAnnotation { background-color: red; position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 144fca9e0..4b949aa3e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -103,22 +103,22 @@ class Viewer extends React.Component { @action componentDidMount = () => { - let wasSelected = this.props.parent.props.isSelected(); + let wasSelected = this.props.parent.props.active(); // reaction for when document gets (de)selected this._reactionDisposer = reaction( - () => [this.props.parent.props.isSelected(), this.startIndex], + () => [this.props.parent.props.active(), this.startIndex], () => { // if deselected, render images in place of pdf - if (wasSelected && !this.props.parent.props.isSelected()) { + if (wasSelected && !this.props.parent.props.active()) { this.saveThumbnail(); this._pointerEvents = "all"; } // if selected, render pdf - else if (!wasSelected && this.props.parent.props.isSelected()) { + else if (!wasSelected && this.props.parent.props.active()) { this.renderPages(this.startIndex, this.endIndex, true); this._pointerEvents = "none"; } - wasSelected = this.props.parent.props.isSelected(); + wasSelected = this.props.parent.props.active(); }, { fireImmediately: true } ); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..fa3f7baca 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -195,7 +195,6 @@ export default class Page extends React.Component { e.stopPropagation(); } else { - e.stopPropagation(); // set marquee x and y positions to the spatially transformed position let current = this._textLayer.current; if (current) { @@ -355,7 +354,7 @@ export default class Page extends React.Component { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From 41c290677030cde827c438d9bfda0dbeac64aa14 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 12:30:24 -0400 Subject: changed selectOnLoad --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4b949aa3e..d6081142a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -586,7 +586,7 @@ class PinAnnotation extends React.Component { PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} focus={emptyFunction} - selectOnLoad={true} + selectOnLoad={false} parentActive={this.props.parent.props.parent.props.active} whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} bringToFront={emptyFunction} -- cgit v1.2.3-70-g09d2 From 94ed67966e7fdc7aa36b1a8b045153d0d661ce57 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 12:50:18 -0400 Subject: early impl of pdf menu --- src/client/views/MainView.tsx | 5 ++- src/client/views/pdf/Page.tsx | 96 +++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 42d5929bf..0779e1471 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -32,6 +32,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Id } from '../../new_fields/FieldSymbols'; import { HistoryUtil } from '../util/History'; import { CollectionBaseView } from './collections/CollectionBaseView'; +import PDFMenu from './pdf/PDFMenu'; @observer @@ -88,6 +89,7 @@ export class MainView extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); + library.add(faCommentAlt); this.initEventListeners(); this.initAuthenticationRouters(); } @@ -315,6 +317,7 @@ export class MainView extends React.Component { {this.nodesMenu()} {this.miscButtons} +
); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..2c237740c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -14,6 +14,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; import { AnnotationTypes } from "./PDFViewer"; +import PDFMenu from "./PDFMenu"; interface IPageProps { pdf: Opt; @@ -272,7 +273,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = (): void => { + onSelectEnd = (e: PointerEvent): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -303,36 +304,79 @@ export default class Page extends React.Component { } this._marqueeHeight = this._marqueeWidth = 0; + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } else { let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - this.props.createAnnotation(annoBox, this.props.page); - } - } - } - // clear selection - if (sel.empty) { // Chrome - sel.empty(); - } else if (sel.removeAllRanges) { // Firefox - sel.removeAllRanges(); - } + if (sel && sel.type === "range") { + + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } } + // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + // if (this._marqueeing) { + // this._marqueeing = false; + // if (this._marquee.current) { + // let copy = document.createElement("div"); + // // make a copy of the marquee + // copy.style.left = this._marquee.current.style.left; + // copy.style.top = this._marquee.current.style.top; + // copy.style.width = this._marquee.current.style.width; + // copy.style.height = this._marquee.current.style.height; + + // // apply the appropriate background, opacity, and transform + // let { background, opacity, transform } = this.getCurlyTransform(); + // copy.style.background = background; + // // if curly bracing, add a curly brace + // if (opacity === "1" && this._curly.current) { + // copy.style.opacity = opacity; + // let img = this._curly.current.cloneNode(); + // (img as any).style.opacity = opacity; + // (img as any).style.transform = transform; + // copy.appendChild(img); + // } + // else { + // copy.style.opacity = this._marquee.current.style.opacity; + // } + // copy.className = this._marquee.current.className; + // this.props.createAnnotation(copy, this.props.page); + // this._marquee.current.style.opacity = "0"; + // } + + // this._marqueeHeight = this._marqueeWidth = 0; + // } + // else { + // let sel = window.getSelection(); + // // if selecting over a range of things + // if (sel && sel.type === "Range") { + // let clientRects = sel.getRangeAt(0).getClientRects(); + // if (this._textLayer.current) { + // let boundingRect = this._textLayer.current.getBoundingClientRect(); + // for (let i = 0; i < clientRects.length; i++) { + // let rect = clientRects.item(i); + // if (rect) { + // let annoBox = document.createElement("div"); + // annoBox.className = "pdfViewer-annotationBox"; + // // transforms the positions from screen onto the pdf div + // annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + // annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + // annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + // annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + // this.props.createAnnotation(annoBox, this.props.page); + // } + // } + // } + // // clear selection + // if (sel.empty) { // Chrome + // sel.empty(); + // } else if (sel.removeAllRanges) { // Firefox + // sel.removeAllRanges(); + // } + // } + // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } -- cgit v1.2.3-70-g09d2 From 0b4f3c25471e51d27ddb33dc5ddafafb2c0c03e5 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 13:09:15 -0400 Subject: fixes for full screen. --- src/client/views/nodes/PDFBox.tsx | 3 ++- src/client/views/pdf/PDFViewer.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index acb430deb..cce7b2631 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -65,7 +65,8 @@ export class PDFBox extends DocComponent(PdfDocumen return (
e.stopPropagation()} className={classname}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d6081142a..440a20e8e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -111,14 +111,13 @@ class Viewer extends React.Component { // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.active()) { this.saveThumbnail(); - this._pointerEvents = "all"; } // if selected, render pdf else if (!wasSelected && this.props.parent.props.active()) { this.renderPages(this.startIndex, this.endIndex, true); - this._pointerEvents = "none"; } wasSelected = this.props.parent.props.active(); + this._pointerEvents = wasSelected ? "none" : "all"; }, { fireImmediately: true } ); -- cgit v1.2.3-70-g09d2 From 2ef1dd2089ad991f1d4897022f2d28c2eb130837 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 15:47:43 -0400 Subject: highlighting --- src/client/views/nodes/PDFBox.tsx | 10 +++++- src/client/views/pdf/PDFMenu.tsx | 25 +++++++++++-- src/client/views/pdf/PDFViewer.tsx | 6 ++-- src/client/views/pdf/Page.tsx | 74 ++++++++++++++++++++++++++++---------- 4 files changed, 91 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index acb430deb..6ee62bbad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -34,8 +34,16 @@ export class PDFBox extends DocComponent(PdfDocumen loaded = (nw: number, nh: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + let oldnw = NumCast(doc.nativeWidth); doc.nativeWidth = nw; - doc.nativeHeight = nh; + if (!doc.nativeHeight) { + doc.nativeHeight = nh; + } + else { + let oldnh = NumCast(doc.nativeHeight); + let aspect = oldnh / oldnw; + doc.nativeHeight = nw * aspect; + } let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.pdfHeight = nh; diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index cc5c0b77b..a0230113b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -3,6 +3,8 @@ import "./PDFMenu.scss"; import { observable } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { emptyFunction } from "../../../Utils"; +import { Doc } from "../../../new_fields/Doc"; @observer export default class PDFMenu extends React.Component { @@ -10,6 +12,8 @@ export default class PDFMenu extends React.Component { @observable Top: number = 0; @observable Left: number = 0; + StartDrag: (e: PointerEvent) => void = emptyFunction; + Highlight: (d: Doc | undefined) => void = emptyFunction; constructor(props: Readonly<{}>) { super(props); @@ -17,11 +21,28 @@ export default class PDFMenu extends React.Component { PDFMenu.Instance = this; } + pointerDown = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.addEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp) + document.addEventListener("pointerup", this.pointerUp) + + e.stopPropagation(); + e.preventDefault(); + } + + pointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp); + e.stopPropagation(); + e.preventDefault(); + } + render() { return (
- - + +
) } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d6081142a..fbad39880 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,7 +152,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -171,7 +171,9 @@ class Viewer extends React.Component { let annoDoc = new Doc(); annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + if (sourceDoc) { + DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + } this._savedAnnotations.clear(); return annoDoc; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bdb6952cc..44c502a04 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -28,7 +28,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined) => Doc; } @observer @@ -132,6 +132,20 @@ export default class Page extends React.Component { } } + highlight = (targetDoc: Doc | undefined) => { + // creates annotation documents for current highlights + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(annotationDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([annotationDoc]); + } + return annotationDoc; + } + /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -149,16 +163,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { - targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); - } + let annotationDoc = this.highlight(targetDoc); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -173,8 +178,8 @@ export default class Page extends React.Component { // cleans up events and boolean endDrag = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); + // document.removeEventListener("pointermove", this.startDrag); + // document.removeEventListener("pointerup", this.endDrag); this._dragging = false; e.stopPropagation(); } @@ -185,10 +190,10 @@ export default class Page extends React.Component { if (e.altKey && e.button === 0) { e.stopPropagation(); - document.removeEventListener("pointermove", this.startDrag); - document.addEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - document.addEventListener("pointerup", this.endDrag); + // document.removeEventListener("pointermove", this.startDrag); + // document.addEventListener("pointermove", this.startDrag); + // document.removeEventListener("pointerup", this.endDrag); + // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { let target: any = e.target; @@ -299,6 +304,8 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } @@ -308,8 +315,10 @@ export default class Page extends React.Component { } else { let sel = window.getSelection(); - if (sel && sel.type === "range") { - + if (sel && sel.type === "Range") { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + this.createTextAnnotation(sel); PDFMenu.Instance.Left = e.clientX; PDFMenu.Instance.Top = e.clientY; } @@ -380,6 +389,33 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); } + @action + createTextAnnotation = (sel: Selection) => { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + this.props.createAnnotation(annoBox, this.props.page); + } + } + } + // clear selection + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + doubleClick = (e: React.MouseEvent) => { let target: any = e.target; // if double clicking text -- cgit v1.2.3-70-g09d2 From 15e8341334419142f4a54db23cc643f18ba0e0f1 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 15:53:13 -0400 Subject: a few tweaks to fix unfreezing documents to give them a margin --- src/client/views/collections/CollectionDockingView.tsx | 8 ++++++-- src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/PDFBox.tsx | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 51e29cb54..235bf5ae4 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -9,7 +9,7 @@ import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; import { Id } from '../../../new_fields/FieldSymbols'; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocumentManager } from '../../util/DocumentManager'; @@ -429,7 +429,11 @@ export class DockedFrameRenderer extends React.Component { } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); - nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight); + nativeHeight = () => { + let nh = NumCast(this._document!.nativeHeight, this._panelHeight); + let res = BoolCast(this._document!.ignoreAspect) ? this._panelHeight : nh; + return res; + } contentScaling = () => { const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6fe01963a..583fa3e1a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -452,8 +452,9 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + let ph = this.props.PanelHeight(); + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
(PdfDocumen loaded = (nw: number, nh: number) => { if (this.props.Document) { + if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; - doc.nativeHeight = nh; + if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; + else doc.nativeHeight = nh; let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.pdfHeight = nh; -- cgit v1.2.3-70-g09d2 From f6e8b7a0f8a13ddf059cf701e46b8cbb8d9228f7 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 17:27:49 -0400 Subject: added page fwd/back/goto --- src/client/views/collections/CollectionPDFView.tsx | 48 ++++++----------- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/FieldView.tsx | 2 + src/client/views/nodes/PDFBox.tsx | 62 ++++++++++++++++------ src/client/views/pdf/PDFViewer.tsx | 6 +-- src/client/views/pdf/Page.tsx | 12 ++--- 6 files changed, 72 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 4af89d780..b62d3f7bb 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,19 +1,21 @@ -import { action, observable, IReactionDisposer, reaction } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; +import { WidthSym } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { NumCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { ContextMenu } from "../ContextMenu"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import "./CollectionPDFView.scss"; import React = require("react"); -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; -import { emptyFunction } from "../../../Utils"; -import { NumCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { PDFBox } from "../nodes/PDFBox"; @observer export class CollectionPDFView extends React.Component { + private _pdfBox?: PDFBox; private _reactionDisposer?: IReactionDisposer; private _buttonTray: React.RefObject; @@ -46,31 +48,12 @@ export class CollectionPDFView extends React.Component { } @observable _inThumb = false; - private set curPage(value: number) { this.props.Document.curPage = value; } + private set curPage(value: number) { this._pdfBox && this._pdfBox.GotoPage(value); } private get curPage() { return NumCast(this.props.Document.curPage, -1); } private get numPages() { return NumCast(this.props.Document.numPages); } - @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; - @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; + @action onPageBack = () => this._pdfBox && this._pdfBox.BackPage(); + @action onPageForward = () => this._pdfBox && this._pdfBox.ForwardPage(); - @action - onThumbDown = (e: React.PointerEvent) => { - document.addEventListener("pointermove", this.onThumbMove, false); - document.addEventListener("pointerup", this.onThumbUp, false); - e.stopPropagation(); - this._inThumb = true; - } - @action - onThumbMove = (e: PointerEvent) => { - let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height; - this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1)); - e.stopPropagation(); - } - @action - onThumbUp = (e: PointerEvent) => { - this._inThumb = false; - document.removeEventListener("pointermove", this.onThumbMove); - document.removeEventListener("pointerup", this.onThumbUp); - } nativeWidth = () => NumCast(this.props.Document.nativeWidth); nativeHeight = () => NumCast(this.props.Document.nativeHeight); private get uIButtons() { @@ -92,11 +75,14 @@ export class CollectionPDFView extends React.Component { } } + setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; }; + + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; return ( <> - + {renderProps.active() ? this.uIButtons : (null)} ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 583fa3e1a..0d5df550a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -453,7 +453,6 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - let ph = this.props.PanelHeight(); var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
number; PanelHeight: () => number; setVideoBox?: (player: VideoBox) => void; + setPdfBox?: (player: PDFBox) => void; } @observer diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b9ccd79e4..243982a3b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,25 +1,22 @@ -import * as htmlToImage from "html-to-image"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; -import Measure from "react-measure"; +import { WidthSym } from "../../../new_fields/Doc"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { PdfField } from "../../../new_fields/URLField"; //@ts-ignore // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { makeInterface } from "../../../new_fields/Schema"; -import { PDFViewer } from "../pdf/PDFViewer"; -import { PdfField } from "../../../new_fields/URLField"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -31,19 +28,50 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; private _reactionDisposer?: IReactionDisposer; - _targetDiv: any = undefined; - componentDidMount: () => void = () => { + componentDidMount() { + if (this.props.setPdfBox) this.props.setPdfBox(this); + } + + public GetPage() { + return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + } + public BackPage() { + let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + cp = cp - 1; + if (cp > 0) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + public GotoPage(p: number) { + if (p > 0 && p <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = p; + this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight); + } + } + + public ForwardPage() { + let cp = this.GetPage() + 1; + if (cp <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + + createRef = (ele: HTMLDivElement | null) => { if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => - this._targetDiv && this._targetDiv.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }) - ); + this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => { + ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + }); } - loaded = (nw: number, nh: number) => { + loaded = (nw: number, nh: number, np: number) => { if (this.props.Document) { - if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log("pages = " + np); + doc.numPages = np; + if (doc.nativeWidth && doc.nativeHeight) return; let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; @@ -59,7 +87,6 @@ export class PDFBox extends DocComponent(PdfDocumen @action onScroll = (e: React.UIEvent) => { if (e.currentTarget) { - this._targetDiv = e.currentTarget; this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); let ccv = this.props.ContainingCollectionView; @@ -82,6 +109,7 @@ export class PDFBox extends DocComponent(PdfDocumen overflowY: "scroll", overflowX: "hidden", marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} + ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 17f65c7a6..dee891ba6 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,7 +23,7 @@ import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; } @@ -61,7 +61,7 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; mainCont: React.RefObject; @@ -400,7 +400,7 @@ class Viewer extends React.Component { return; } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.props.loaded(page.width, page.height); + this.props.loaded(page.width, page.height, numPages); this._pageSizes[index - 1] = { width: page.width, height: page.height }; this._pagesLoaded++; if (this._pagesLoaded === numPages) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..e3dbeaebe 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,16 +132,14 @@ export default class Page extends React.Component { } } - highlight = (targetDoc: Doc | undefined) => { + highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { + let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (targetAnnotations === undefined) { + Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); + } else { targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); } return annotationDoc; } -- cgit v1.2.3-70-g09d2 From b987e2edbf7befbe90fafbdee476ee3b6513cc50 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 17:44:17 -0400 Subject: pinning things --- src/client/views/MainView.tsx | 3 +- src/client/views/pdf/PDFMenu.scss | 13 ++++- src/client/views/pdf/PDFMenu.tsx | 119 +++++++++++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 21 ++++--- 4 files changed, 137 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 384cd2860..29015995f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -92,6 +92,7 @@ export class MainView extends React.Component { library.add(faMusic); library.add(faTree); library.add(faCommentAlt); + library.add(faThumbtack); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss index b84ebc12d..22868082a 100644 --- a/src/client/views/pdf/PDFMenu.scss +++ b/src/client/views/pdf/PDFMenu.scss @@ -1,18 +1,25 @@ .pdfMenu-cont { position: absolute; z-index: 10000; - width: 100px; - height: 30px; + height: 35px; background: #323232; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - border-radius: 0px 4px 4px 4px; + border-radius: 0px 6px 6px 6px; overflow: hidden; + display: flex; .pdfMenu-button { background-color: transparent; + width: 35px; + height: 35px; } .pdfMenu-button:hover { background-color: #121212; } + + .pdfMenu-dragger { + height: 100%; + transition: width .2s; + } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index a0230113b..d2a20fb6e 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import "./PDFMenu.scss"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { emptyFunction } from "../../../Utils"; @@ -10,10 +10,18 @@ import { Doc } from "../../../new_fields/Doc"; export default class PDFMenu extends React.Component { static Instance: PDFMenu; - @observable Top: number = 0; - @observable Left: number = 0; + @observable private _top: number = 0; + @observable private _left: number = 0; + @observable private _opacity: number = 1; + @observable private _transition: string = "opacity 0.5s"; + @observable private _transitionDelay: string = ""; + @observable private _pinned: boolean = false; + StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined) => void = emptyFunction; + @observable Highlighting: boolean = false; + + private _timeout: NodeJS.Timeout | undefined; constructor(props: Readonly<{}>) { super(props); @@ -38,12 +46,109 @@ export default class PDFMenu extends React.Component { e.preventDefault(); } + @action + jumpTo = (x: number, y: number) => { + if (!this._pinned) { + this._transition = this._transitionDelay = ""; + this._opacity = 1; + this._left = x; + this._top = y; + } + } + + @action + fadeOut = (forceOut: boolean) => { + if (!this._pinned) { + if (this._opacity === 0.2) { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + + if (forceOut) { + this._transition = ""; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + } + } + + @action + pointerLeave = (e: React.PointerEvent) => { + if (!this._pinned) { + this._transition = "opacity 0.5s"; + this._transitionDelay = "1s"; + this._opacity = 0.2; + setTimeout(() => this.fadeOut(false), 3000); + } + } + + @action + pointerEntered = (e: React.PointerEvent) => { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 1; + } + + @action + togglePin = (e: React.MouseEvent) => { + this._pinned = !this._pinned; + } + + @action + dragging = (e: PointerEvent) => { + this._left += e.movementX; + this._top += e.movementY; + + e.stopPropagation(); + e.preventDefault(); + } + + dragEnd = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + e.stopPropagation(); + e.preventDefault(); + } + + dragStart = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.addEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + document.addEventListener("pointerup", this.dragEnd); + + e.stopPropagation(); + e.preventDefault(); + } + + @action + highlightClicked = (e: React.MouseEvent) => { + if (!this._pinned) { + this.Highlight(undefined); + } + else { + this.Highlighting = !this.Highlighting; + this.Highlight(undefined); + } + } + render() { return ( -
- - -
+
+ + + +
+
) } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..3bc3ac33c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,6 +132,7 @@ export default class Page extends React.Component { } } + @action highlight = (targetDoc: Doc | undefined) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); @@ -196,6 +197,7 @@ export default class Page extends React.Component { // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { + PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); @@ -304,25 +306,28 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); if (sel && sel.type === "Range") { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this.createTextAnnotation(sel); - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } } + + + if (PDFMenu.Instance.Highlighting) { + this.highlight(undefined); + } + else { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + } // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); // if (this._marqueeing) { -- cgit v1.2.3-70-g09d2 From 747fdb6d26afabad067f3f8d98789bfc7ca44f8d Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 18:11:41 -0400 Subject: small stuffs --- src/client/views/pdf/PDFMenu.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 13 +++++++------ src/client/views/pdf/Page.tsx | 13 ++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index d2a20fb6e..2ba875e42 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -95,6 +95,7 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { this._pinned = !this._pinned; + this.Highlighting = this._pinned === false; } @action diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index dee891ba6..55d15893a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -21,6 +21,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { DragManager } from "../../util/DragManager"; import { Dictionary } from "typescript-collections"; +export const scale = 2; interface IPDFViewerProps { url: string; loaded: (nw: number, nh: number, np: number) => void; @@ -156,10 +157,10 @@ class Viewer extends React.Component { this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { let annoDoc = new Doc(); - 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); - if (anno.style.width) annoDoc.width = parseInt(anno.style.width); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; + if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale; + if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale; + if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; annoDoc.type = AnnotationTypes.Region; @@ -572,7 +573,7 @@ class PinAnnotation extends React.Component {
{ render() { return (
+ style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: this._backgroundColor }}>
); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 455e1d831..bd2cae749 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,9 +13,10 @@ import { emptyFunction } from "../../../Utils"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; -import { AnnotationTypes } from "./PDFViewer"; +import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; import PDFMenu from "./PDFMenu"; + interface IPageProps { pdf: Opt; name: string; @@ -28,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; } @observer @@ -103,7 +104,6 @@ export default class Page extends React.Component { @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes - let scale = 2; let viewport = page.getViewport(scale); let canvas = this._canvas.current; let textLayer = this._textLayer.current; @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -307,8 +307,11 @@ export default class Page extends React.Component { this._marquee.current.style.opacity = "0"; } + if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + } + this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); -- cgit v1.2.3-70-g09d2 From d9fba98b4022d2be68f67d830f17fea79580d2c4 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 18:13:18 -0400 Subject: small --- src/client/views/pdf/PDFMenu.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2ba875e42..b0735f63b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -95,7 +95,9 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { this._pinned = !this._pinned; - this.Highlighting = this._pinned === false; + if (!this._pinned) { + this.Highlighting = false; + } } @action -- cgit v1.2.3-70-g09d2 From 5a6a066490c09dfe21fbdacbe331de9bbe622173 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 14 Jun 2019 22:01:00 -0400 Subject: fixed hyperlink following in formattedtextbox's --- src/client/views/nodes/FormattedTextBox.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e5a43c60a..3c590bd82 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -30,6 +30,7 @@ import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { DocumentManager } from '../../util/DocumentManager'; library.add(faEdit); library.add(faSmile); @@ -241,6 +242,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (href) { if (href.indexOf(DocServer.prepend("/doc/")) === 0) { this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; + if (this._linkClicked) { + DocServer.GetRefField(this._linkClicked).then(f => { + (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")); + }); + e.stopPropagation(); + e.preventDefault(); + } } else { let webDoc = Docs.WebDocument(href, { x: NumCast(this.props.Document.x, 0) + NumCast(this.props.Document.width, 0), y: NumCast(this.props.Document.y) }); this.props.addDocument && this.props.addDocument(webDoc); @@ -283,6 +291,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onClick = (e: React.MouseEvent): void => { this._proseRef!.focus(); if (this._linkClicked) { + this._linkClicked = ""; e.preventDefault(); e.stopPropagation(); } -- cgit v1.2.3-70-g09d2 From 6763fc89d2ba9782d88a7677661dbe5ae27ee097 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 15 Jun 2019 21:41:29 -0400 Subject: several fixes to tree view --- .../views/collections/CollectionTreeView.scss | 30 ++++++++-------------- .../views/collections/CollectionTreeView.tsx | 16 +++++++----- 2 files changed, 21 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2dc4b2e80..2568bf182 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -25,12 +25,10 @@ } .bullet { - float: left; position: relative; width: 15px; - display: block; color: $intermediate-color; - margin-top: 8px; + margin-top: 4px; transform: scale(1.3, 1.3); } .editableView-container { @@ -46,13 +44,6 @@ } - .docContainer:hover { - .delete-button { - display: inline; - // width: auto; - } - } - .coll-title { width: max-content; display: block; @@ -66,23 +57,23 @@ } .docContainer { - margin-left: 10px; - display: block; + position: relative; + text-overflow: ellipsis; + white-space: pre-wrap; + overflow: hidden; // width:100%;//width: max-content; - .treeViewItem-openRight { - margin-left: 5px; - display: none; - } } -#docContainer-data { - margin-top: 5px; +.treeViewItem-openRight { + display: none; } -.docContainer:hover { +.treeViewItem-header:hover { .treeViewItem-openRight { display: inline-block; height:13px; + margin-top:2px; + margin-left: 5px; // display: inline; svg { display:block; @@ -94,6 +85,7 @@ .treeViewItem-header { border: transparent 1px solid; + display:flex; } .treeViewItem-header-above { border: transparent 1px solid; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8a6764c58..65b364c47 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -149,7 +149,7 @@ class TreeView extends React.Component { {/* */}
); - return ( + return <>
{ }} > {editableView(StrCast(this.props.document.title))} - {openRight} {/* {
} */} -
); +
+ {openRight} + ; } onWorkspaceContextMenu = (e: React.MouseEvent): void => { @@ -229,17 +230,20 @@ class TreeView extends React.Component { keys.push(...Array.from(Object.keys(this.props.document.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } + if (keys.indexOf("data") !== -1) { + keys.splice(keys.indexOf("data"), 1); + keys.splice(0, 0, "data"); + } keys.map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before); let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || docList) { + if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { if (!this._collapsed) { bulletType = BulletType.Collapsible; contentElement.push(
    - {(key === "data") ? (null) : - {key}} + {key}
    {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)} -- cgit v1.2.3-70-g09d2 From c3b1877b4d17715416267ff3601cc982ca3b1535 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 15 Jun 2019 22:46:40 -0400 Subject: partial fix for dragging tree view items --- src/client/views/collections/CollectionTreeView.scss | 3 --- src/client/views/collections/CollectionTreeView.tsx | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2568bf182..f6df96d92 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -88,14 +88,11 @@ display:flex; } .treeViewItem-header-above { - border: transparent 1px solid; border-top: black 1px solid; } .treeViewItem-header-below { - border: transparent 1px solid; border-bottom: black 1px solid; } .treeViewItem-header-inside { - border: transparent 1px solid; border: black 1px solid; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 65b364c47..809b2af2b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -71,6 +71,7 @@ class TreeView extends React.Component { @action onMouseLeave = () => { this._isOver = false; } onPointerEnter = (e: React.PointerEvent): void => { + console.log("ENTER = " + this.props.document.title); this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1) { this._header!.current!.className = "treeViewItem-header"; @@ -90,9 +91,9 @@ class TreeView extends React.Component { let before = x[1] < bounds[1]; let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); this._header!.current!.className = "treeViewItem-header" - if (inside && this._bulletType != BulletType.List) this._header!.current!.className = "treeViewItem-header-inside"; - else if (before) this._header!.current!.className = "treeViewItem-header-above"; - else if (!before) this._header!.current!.className = "treeViewItem-header-below"; + if (inside && this._bulletType != BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + else if (before) this._header!.current!.className += " treeViewItem-header-above"; + else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); } onPointerDown = (e: React.PointerEvent) => { -- cgit v1.2.3-70-g09d2 From b681e584f52a9ffe03c9d93b199b6487e5d1a5e5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 15 Jun 2019 23:07:45 -0400 Subject: fixed dragging within treeview. --- src/client/util/DragManager.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ab00fabe1..88027a601 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -261,6 +261,7 @@ export namespace DragManager { // } // } let set = dragElement.getElementsByTagName('*'); + if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none"; for (let i = 0; i < set.length; i++) if (set[i].hasAttribute("style")) { let s = set[i]; -- cgit v1.2.3-70-g09d2 From 19c3ffc07038e6d5f5dbaa1b789f2799a2958985 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 15 Jun 2019 23:08:07 -0400 Subject: from last --- src/client/views/collections/CollectionTreeView.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 809b2af2b..659cb2f28 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -71,7 +71,6 @@ class TreeView extends React.Component { @action onMouseLeave = () => { this._isOver = false; } onPointerEnter = (e: React.PointerEvent): void => { - console.log("ENTER = " + this.props.document.title); this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1) { this._header!.current!.className = "treeViewItem-header"; -- cgit v1.2.3-70-g09d2 From 3c60aeab5c4022e1150d277a14238f96d3738c1f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 16 Jun 2019 01:02:54 -0400 Subject: tweak --- src/client/views/collections/CollectionStackingView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index cad7cd50c..236bfe136 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -22,7 +22,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); } - @computed get columnWidth() { return this.singleColumn ? this.props.PanelWidth() - 2 * this.xMargin : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } + @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } singleColDocHeight(d: Doc) { let nw = NumCast(d.nativeWidth); -- cgit v1.2.3-70-g09d2 From 4d3d9b4c33be1df26144aea16dde060b9263924e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 16 Jun 2019 17:20:25 -0400 Subject: Added check for Docking Instance initializtion --- src/client/views/collections/CollectionDockingView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 51e29cb54..0ad82f345 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -48,7 +48,10 @@ export class CollectionDockingView extends React.Component Date: Sun, 16 Jun 2019 19:08:08 -0400 Subject: better virtualization --- package.json | 1 + src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 47 ++++++++------ src/server/index.ts | 122 ++++++++++++++++++++++++++++--------- 4 files changed, 122 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 0fe26f16d..7fd6c4ba9 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "passport": "^0.4.0", "passport-local": "^1.0.0", "pdfjs-dist": "^2.0.943", + "probe-image-size": "^4.0.0", "prosemirror-commands": "^1.0.7", "prosemirror-example-setup": "^1.0.1", "prosemirror-history": "^1.0.4", diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 243982a3b..655c12ab3 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -111,7 +111,7 @@ export class PDFBox extends DocComponent(PdfDocumen }} ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> - + {/*
    */}
    ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 55d15893a..75c298f55 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -20,6 +20,9 @@ import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; import { DocumentView } from "../nodes/DocumentView"; import { DragManager } from "../../util/DragManager"; import { Dictionary } from "typescript-collections"; +import * as rp from "request-promise"; +import { restProperty } from "babel-types"; +import { DocServer } from "../../DocServer"; export const scale = 2; interface IPDFViewerProps { @@ -137,7 +140,8 @@ class Viewer extends React.Component { } setTimeout(() => { - this.renderPages(this.startIndex, this.endIndex, true); + // this.renderPages(this.startIndex, this.endIndex, true); + this.saveThumbnail(); }, 1000); } @@ -204,17 +208,19 @@ class Viewer extends React.Component { } @action - saveThumbnail = () => { + saveThumbnail = async () => { // file address of the pdf const address: string = this.props.url; for (let i = 0; i < this._visibleElements.length; i++) { if (this._isPage[i]) { // change the address to be the file address of the PNG version of each page - let thisAddress = `${address.substring(0, address.length - ".pdf".length)}-${i + 1}.PNG`; - let nWidth = this._pageSizes[i].width; - let nHeight = this._pageSizes[i].height; + let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`))); + let thisAddress = res.path; + let nWidth = parseInt(res.width); + let nHeight = parseInt(res.height); // replace page with image - this._visibleElements[i] = ; + runInAction(() => + this._visibleElements[i] = ); } } } @@ -236,6 +242,7 @@ class Viewer extends React.Component { if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { this._pdf = this.props.pdf; // render pages if the scorll position changes + console.log(`START: ${this.startIndex}, END: ${this.endIndex}`); this.renderPages(this.startIndex, this.endIndex); } } @@ -269,7 +276,7 @@ class Viewer extends React.Component { // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { - let divs = Array.from(Array(numPages).keys()).map(i => ( + let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? ( { makeAnnotationDocuments={this.makeAnnotationDocument} receiveAnnotations={this.sendAnnotations} {...this.props} /> - )); - let arr = Array.from(Array(numPages).keys()).map(() => false); + ) : + (
    ) + ); + let arr = Array.from(Array(numPages).keys()).map(i => i < 5); this._visibleElements.push(...divs); this._isPage.push(...arr); } @@ -378,16 +387,16 @@ class Viewer extends React.Component { // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { - if (this._loaded) { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - let index = 0; - let currOffset = vOffset; - while (index < numPages && currOffset - this._pageSizes[index].height > 0) { - currOffset -= this._pageSizes[index].height; - index++; - } - return index; - } + // if (this._loaded) { + let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + let index = 0; + let currOffset = vOffset; + while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { + currOffset -= this._pageSizes[index].height; + index++; + } + return index; + // } return 0; } diff --git a/src/server/index.ts b/src/server/index.ts index e22794df1..3e51eb4ff 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -38,8 +38,10 @@ import c = require("crypto"); import { Search } from './Search'; import { debug } from 'util'; import _ = require('lodash'); +import { Response } from 'express-serve-static-core'; const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); +const probe = require("probe-image-size"); const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest)); @@ -134,6 +136,66 @@ app.get("/search", async (req, res) => { res.send(results); }); +app.get("/thumbnail/:filename", (req, res) => { + let filename = req.params.filename; + let noExt = filename.substring(0, filename.length - ".png".length); + let pagenumber = parseInt(noExt[noExt.length - 1]); + fs.exists(uploadDir + filename, (exists: boolean) => { + console.log(`${uploadDir + filename} ${exists ? "exists" : "does not exist"}`); + if (exists) { + let input = fs.createReadStream(uploadDir + filename); + probe(input, (err: any, result: any) => { + if (err) { + console.log(err); + return; + } + console.log(result.width); + console.log(result.height); + res.send({ path: "/files/" + filename, width: result.width, height: result.height }); + }); + } + else { + LoadPage(uploadDir + filename.substring(0, filename.length - "-n.png".length) + ".pdf", pagenumber, res); + } + }); +}); + +function LoadPage(file: string, pageNumber: number, res: Response) { + console.log(file); + Pdfjs.getDocument(file).promise + .then((pdf: Pdfjs.PDFDocumentProxy) => { + let factory = new NodeCanvasFactory(); + console.log(pageNumber); + pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => { + console.log("reading " + page); + let viewport = page.getViewport(1); + let canvasAndContext = factory.create(viewport.width, viewport.height); + let renderContext = { + canvasContext: canvasAndContext.context, + viewport: viewport, + canvasFactory: factory + } + console.log("read " + pageNumber); + + page.render(renderContext).promise + .then(() => { + console.log("saving " + pageNumber); + let stream = canvasAndContext.canvas.createPNGStream(); + let pngFile = `${file.substring(0, file.length - ".pdf".length)}-${pageNumber}.PNG`; + let out = fs.createWriteStream(pngFile); + stream.pipe(out); + out.on("finish", () => { + console.log(`Success! Saved to ${pngFile}`); + let name = path.basename(pngFile); + res.send({ path: "/files/" + name, width: viewport.width, height: viewport.height }); + }); + }, (reason: string) => { + console.error(reason + ` ${pageNumber}`); + }); + }); + }); +} + // anyone attempting to navigate to localhost at this port will // first have to login addSecureRoute( @@ -229,36 +291,36 @@ app.post( isImage = true; } else if (pdfTypes.includes(ext)) { - Pdfjs.getDocument(uploadDir + file).promise - .then((pdf: Pdfjs.PDFDocumentProxy) => { - let numPages = pdf.numPages; - let factory = new NodeCanvasFactory(); - for (let pageNum = 0; pageNum < numPages; pageNum++) { - console.log(pageNum); - pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { - console.log("reading " + pageNum); - let viewport = page.getViewport(1); - let canvasAndContext = factory.create(viewport.width, viewport.height); - let renderContext = { - canvasContext: canvasAndContext.context, - viewport: viewport, - canvasFactory: factory - } - console.log("read " + pageNum); - - page.render(renderContext).promise - .then(() => { - console.log("saving " + pageNum); - let stream = canvasAndContext.canvas.createPNGStream(); - let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); - stream.pipe(out); - out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); - }, (reason: string) => { - console.error(reason + ` ${pageNum}`); - }); - }); - } - }); + // Pdfjs.getDocument(uploadDir + file).promise + // .then((pdf: Pdfjs.PDFDocumentProxy) => { + // let numPages = pdf.numPages; + // let factory = new NodeCanvasFactory(); + // for (let pageNum = 0; pageNum < numPages; pageNum++) { + // console.log(pageNum); + // pdf.getPage(pageNum + 1).then((page: Pdfjs.PDFPageProxy) => { + // console.log("reading " + pageNum); + // let viewport = page.getViewport(1); + // let canvasAndContext = factory.create(viewport.width, viewport.height); + // let renderContext = { + // canvasContext: canvasAndContext.context, + // viewport: viewport, + // canvasFactory: factory + // } + // console.log("read " + pageNum); + + // page.render(renderContext).promise + // .then(() => { + // console.log("saving " + pageNum); + // let stream = canvasAndContext.canvas.createPNGStream(); + // let out = fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`); + // stream.pipe(out); + // out.on("finish", () => console.log(`Success! Saved to ${uploadDir + file.substring(0, file.length - ext.length) + `-${pageNum + 1}.PNG`}`)); + // }, (reason: string) => { + // console.error(reason + ` ${pageNum}`); + // }); + // }); + // } + // }); } if (isImage) { resizers.forEach(resizer => { -- cgit v1.2.3-70-g09d2 From e1f88e962cce91d9bcd6900163756934bc5a09a8 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sun, 16 Jun 2019 20:12:33 -0400 Subject: fix --- src/server/index.ts | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/server/index.ts b/src/server/index.ts index 3e51eb4ff..b91c91282 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -149,8 +149,6 @@ app.get("/thumbnail/:filename", (req, res) => { console.log(err); return; } - console.log(result.width); - console.log(result.height); res.send({ path: "/files/" + filename, width: result.width, height: result.height }); }); } -- cgit v1.2.3-70-g09d2 From ee1e6f815b76f9c0e076c8fecc89dd4fa1f5cf4b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 16 Jun 2019 21:39:48 -0400 Subject: fixed access out-of-bounds error.. fixed event handling for unselected pdf. --- src/client/views/nodes/PDFBox.scss | 21 +++++++++++++-------- src/client/views/pdf/PDFViewer.tsx | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index bb1f534c6..8bcae4f1e 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -36,15 +36,14 @@ pointer-events: none; display: flex; flex-direction: row; - - span { - pointer-events: none !important; + .textlayer { + pointer-events: none; + span { + pointer-events: none !important; + } } -} -.textlayer { - span { - pointer-events: all !important; - user-select: text; + .page-cont { + pointer-events: none; } } @@ -52,6 +51,12 @@ pointer-events: all; display: flex; flex-direction: row; + .textlayer { + span { + pointer-events: all !important; + user-select: text; + } + } } .pdfBox-contentContainer { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 75c298f55..bc7cfecbb 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -392,7 +392,7 @@ class Viewer extends React.Component { let index = 0; let currOffset = vOffset; while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { - currOffset -= this._pageSizes[index].height; + currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; index++; } return index; -- cgit v1.2.3-70-g09d2 From 0c2e2cfea58cb95fddb2dc344eff29a8b3dcf7fa Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 10:28:38 -0400 Subject: small change --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8e3c682c7..250b6bf5d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -25,6 +25,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import PDFMenu from "../../pdf/PDFMenu"; export const panZoomSchema = createSchema({ panX: "number", @@ -148,6 +149,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let docs = this.childDocs || []; let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); if (!this.isAnnotationOverlay) { + PDFMenu.Instance.fadeOut(true); let minx = docs.length ? NumCast(docs[0].x) : 0; let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx; let miny = docs.length ? NumCast(docs[0].y) : 0; -- cgit v1.2.3-70-g09d2 From 90c14f3ad8d9e48698c516bc6549143912e19236 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 10:47:40 -0400 Subject: highlights are a different color --- src/client/views/pdf/PDFViewer.tsx | 5 +++-- src/client/views/pdf/Page.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bc7cfecbb..9becfb419 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -156,7 +156,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -167,6 +167,7 @@ class Viewer extends React.Component { if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; + annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); anno.remove(); @@ -624,7 +625,7 @@ class RegionAnnotation extends React.Component { render() { return (
    + 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) }}>
    ); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bd2cae749..e706a0d5c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -29,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc; } @observer @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); -- cgit v1.2.3-70-g09d2 From f7a567b27b5c124e8c3bfe1e866162e2911607c5 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 12:26:57 -0400 Subject: cleaned up minimization code, added minimization for stacking --- .../views/collections/CollectionSchemaView.tsx | 15 --- .../views/collections/CollectionStackingView.tsx | 21 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 147 ++++----------------- src/client/views/nodes/DocumentView.tsx | 62 ++++++++- 4 files changed, 89 insertions(+), 156 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b9e5a5b65..7cc00ce07 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -416,20 +416,6 @@ export class CollectionSchemaPreview extends React.Component) => { this.props.setPreviewScript(e.currentTarget.value); } - @undoBatch - @action - public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.isMinimized = !isMinimized; - }); - } - } render() { let input = this.props.previewScript === undefined ? (null) :
)} {input} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 236bfe136..ef12545b8 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,17 +1,15 @@ import React = require("react"); -import { action, computed, IReactionDisposer, reaction, trace } from "mobx"; +import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { BoolCast, NumCast } from "../../../new_fields/Types"; import { emptyFunction, returnOne, Utils } from "../../../Utils"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch } from "../../util/UndoManager"; +import { ContextMenu } from "../ContextMenu"; import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { ContextMenu } from "../ContextMenu"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -62,20 +60,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { this._masonryGridRef = ele; this.createDropTarget(ele!); } - @undoBatch - @action - public collapseToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.isMinimized = !isMinimized; - }); - } - } @computed get singleColumnChildren() { @@ -144,7 +128,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} bringToFront={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} - collapseToPoint={this.collapseToPoint} />
); }) diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 499b83c0f..f6b1c62ee 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,17 +1,12 @@ -import { computed, IReactionDisposer, reaction, action } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; -import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { OmitKeys } from "../../../Utils"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); -import { UndoManager } from "../../util/UndoManager"; -import { SelectionManager } from "../../util/SelectionManager"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { } @@ -27,13 +22,7 @@ const FreeformDocument = makeInterface(schema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(FreeformDocument) { - private _mainCont = React.createRef(); - _bringToFrontDisposer?: IReactionDisposer; - - @computed get transform() { - return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `; - } - + @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; } @computed get X() { return FieldValue(this.Document.x, 0); } @computed get Y() { return FieldValue(this.Document.y, 0); } @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); } @@ -59,91 +48,20 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom) - - @computed - get docView() { - return ; - } - - componentDidMount() { - this._bringToFrontDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => { - this.props.bringToFront(this.props.Document); - if (values instanceof List) { - let scrpt = this.props.ScreenToLocalTransform().transformPoint(values[0], values[1]); - this.animateBetweenIcon(true, scrpt, [this.Document.x || 0, this.Document.y || 0], - this.Document.width || 0, this.Document.height || 0, values[2], values[3] ? true : false); - } - }, { fireImmediately: true }); - } - - componentWillUnmount() { - if (this._bringToFrontDisposer) this._bringToFrontDisposer(); - } - - static _undoBatch?: UndoManager.Batch = undefined; - @action - public collapseToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise => { - SelectionManager.DeselectAll(); - if (expandedDocs) { - if (!CollectionFreeFormDocumentView._undoBatch) { - CollectionFreeFormDocumentView._undoBatch = UndoManager.StartBatch("iconAnimating"); - } - let isMinimized: boolean | undefined; - expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { - let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); - if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized, false); - } - maximizedDoc.willMaximize = isMinimized; - maximizedDoc.isMinimized = false; - maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); - } + .scale(1 / this.contentScaling()).scale(1 / this.zoom); + + animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { + this.props.bringToFront(this.props.Document); + let targetPos = [this.Document.x || 0, this.Document.y || 0]; + let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]); + DocumentView.animateBetweenIconFunc(this.props.Document, + this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => { + let pval = maximizing ? + [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] : + [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress]; + this.Document.x = progress === 1 ? targetPos[0] : pval[0]; + this.Document.y = progress === 1 ? targetPos[1] : pval[1]; }); - setTimeout(() => { - CollectionFreeFormDocumentView._undoBatch && CollectionFreeFormDocumentView._undoBatch.end(); - CollectionFreeFormDocumentView._undoBatch = undefined; - }, 500); - } - } - - animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, maximizing: boolean) { - - setTimeout(() => { - let now = Date.now(); - let progress = Math.min(1, (now - stime) / 200); - let pval = maximizing ? - [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] : - [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress]; - this.props.Document.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress; - this.props.Document.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; - this.props.Document.x = pval[0]; - this.props.Document.y = pval[1]; - if (first) { - this.props.Document.proto!.willMaximize = false; - } - if (now < stime + 200) { - this.animateBetweenIcon(false, icon, targ, width, height, stime, maximizing); - } - else { - if (!maximizing) { - this.props.Document.proto!.isMinimized = true; - this.props.Document.x = targ[0]; - this.props.Document.y = targ[1]; - this.props.Document.width = width; - this.props.Document.height = height; - } - this.props.Document.proto!.isIconAnimating = undefined; - } - }, - 2); } borderRounding = () => { @@ -155,34 +73,25 @@ export class CollectionFreeFormDocumentView extends DocComponent 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1; - const screenWidth = Math.min(50 * NumCast(this.props.Document.nativeWidth, 0), 1800); - let fadeUp = .75 * screenWidth; - let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth; - // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; - return ( -
- {this.docView} +
); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6fe01963a..a8dc13d1e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -75,7 +75,7 @@ export interface DocumentViewProps { whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; addDocTab: (doc: Doc, where: string) => void; - collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; + animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; } const schema = createSchema({ @@ -127,6 +127,7 @@ export class DocumentView extends DocComponent(Docu super(props); } + _animateToIconDisposer?: IReactionDisposer; _reactionDisposer?: IReactionDisposer; @action componentDidMount() { @@ -148,8 +149,35 @@ export class DocumentView extends DocComponent(Docu this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded"; } }, { fireImmediately: true }); + this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => + (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false) + , { fireImmediately: true }); DocumentManager.Instance.DocumentViews.push(this); } + + animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { + this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) : + DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing); + Doc.GetProto(this.props.Document).willMaximize = false; + } + + public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { + setTimeout(() => { + let now = Date.now(); + let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1 + doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress + doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; + cb && cb(progress); + if (now < stime + 200) { + DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb); + } + else { + Doc.GetProto(doc).isMinimized = !maximizing; + Doc.GetProto(doc).isIconAnimating = undefined; + } + }, + 2); + } @action componentDidUpdate() { if (this._dropDisposer) { @@ -164,6 +192,7 @@ export class DocumentView extends DocComponent(Docu @action componentWillUnmount() { if (this._reactionDisposer) this._reactionDisposer(); + if (this._animateToIconDisposer) this._animateToIconDisposer(); if (this._dropDisposer) this._dropDisposer(); DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); } @@ -195,7 +224,34 @@ export class DocumentView extends DocComponent(Docu if (minimizedDoc) { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint( NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y)); - this.props.collapseToPoint && this.props.collapseToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)); + this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)) + } + } + + static _undoBatch?: UndoManager.Batch = undefined; + @action + public collapseTargetsToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise => { + SelectionManager.DeselectAll(); + if (expandedDocs) { + if (!DocumentView._undoBatch) { + DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating"); + } + let isMinimized: boolean | undefined; + expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => { + let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); + if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { + if (isMinimized === undefined) { + isMinimized = BoolCast(maximizedDoc.isMinimized, false); + } + maximizedDoc.willMaximize = isMinimized; + maximizedDoc.isMinimized = false; + maximizedDoc.isIconAnimating = new List([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); + } + }); + setTimeout(() => { + DocumentView._undoBatch && DocumentView._undoBatch.end(); + DocumentView._undoBatch = undefined; + }, 500); } } @@ -251,7 +307,7 @@ export class DocumentView extends DocComponent(Docu } } else { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); - this.props.collapseToPoint && this.props.collapseToPoint(scrpt, expandedProtoDocs); + this.collapseTargetsToPoint(scrpt, expandedProtoDocs); } } else if (linkedToDocs.length || linkedFromDocs.length) { -- cgit v1.2.3-70-g09d2 From 589d2409cf00c3ff15eddbe88835a63a09785f2c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 12:36:15 -0400 Subject: from last --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a8dc13d1e..856fdab7f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -158,7 +158,6 @@ export class DocumentView extends DocComponent(Docu animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) : DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing); - Doc.GetProto(this.props.Document).willMaximize = false; } public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { @@ -175,6 +174,7 @@ export class DocumentView extends DocComponent(Docu Doc.GetProto(doc).isMinimized = !maximizing; Doc.GetProto(doc).isIconAnimating = undefined; } + Doc.GetProto(doc).willMaximize = false; }, 2); } -- cgit v1.2.3-70-g09d2 From 122607408882617235af255af84ce78828b7982f Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 13:42:21 -0400 Subject: page sizes loaded --- src/client/views/pdf/PDFMenu.tsx | 6 +++--- src/client/views/pdf/PDFViewer.tsx | 30 +++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 8 ++++---- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index b0735f63b..b44370e3d 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -18,7 +18,7 @@ export default class PDFMenu extends React.Component { @observable private _pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; - Highlight: (d: Doc | undefined) => void = emptyFunction; + Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; @observable Highlighting: boolean = false; private _timeout: NodeJS.Timeout | undefined; @@ -129,11 +129,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this._pinned) { - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 9becfb419..d74a16f3f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,6 +23,7 @@ import { Dictionary } from "typescript-collections"; import * as rp from "request-promise"; import { restProperty } from "babel-types"; import { DocServer } from "../../DocServer"; +import { number } from "prop-types"; export const scale = 2; interface IPDFViewerProps { @@ -141,10 +142,33 @@ class Viewer extends React.Component { setTimeout(() => { // this.renderPages(this.startIndex, this.endIndex, true); - this.saveThumbnail(); + this.initialLoad(); }, 1000); } + @action + initialLoad = () => { + let pdf = this.props.pdf; + if (pdf) { + this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages); + let rendered = 0; + for (let i = 0; i < pdf.numPages; i++) { + pdf.getPage(i + 1).then( + (page: Pdfjs.PDFPageProxy) => { + runInAction(() => { + this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }; + }); + console.log(`page ${i} size retreieved`); + rendered++; + if (rendered === pdf!.numPages - 1) { + this.saveThumbnail(); + } + } + ); + } + } + } + private mainCont = (div: HTMLDivElement | null) => { if (this._dropDisposer) { this._dropDisposer(); @@ -186,7 +210,7 @@ class Viewer extends React.Component { drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocument(sourceDoc); + let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red"); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(destDoc); @@ -392,7 +416,7 @@ class Viewer extends React.Component { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; let index = 0; let currOffset = vOffset; - while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { + while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; index++; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index e706a0d5c..bb87ec9d4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -133,9 +133,9 @@ export default class Page extends React.Component { } @action - highlight = (targetDoc?: Doc) => { + highlight = (targetDoc?: Doc, color: string = "red") => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -162,7 +162,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - let annotationDoc = this.highlight(targetDoc); + let annotationDoc = this.highlight(targetDoc, "red"); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -323,7 +323,7 @@ export default class Page extends React.Component { if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined); + this.highlight(undefined, "#f4f442"); } else { PDFMenu.Instance.StartDrag = this.startDrag; -- cgit v1.2.3-70-g09d2 From f74e512e500252ad76d77935e7aacbf72cb0dd9c Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 14:41:21 -0400 Subject: added dropping on text as colleciton. --- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 30 ++++++++++------------ .../views/collections/CollectionStackingView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FormattedTextBox.tsx | 28 ++++++++++++++++++-- 5 files changed, 44 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 3288abd90..a9bc4d3d2 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -45,7 +45,7 @@ export class TemplateMenu extends React.Component { if (template.Name === "Bullet") { let topDocView = this.props.docs[0]; topDocView.addTemplate(template); - topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!)); + topDocView.props.Document.subBulletDocs = new List(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document)); } else { this.props.docs.map(d => d.addTemplate(template)); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 7cc00ce07..4b46c73c1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -2,36 +2,33 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked, runInAction, trace } from "mobx"; +import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; -import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import "react-table/react-table.css"; +import { Doc, DocListCast, DocListCastAsync, Field } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { List } from "../../../new_fields/List"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnFalse, returnZero } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { Gateway } from "../../northstar/manager/Gateway"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; -import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss"; +import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; +import { ContextMenu } from "../ContextMenu"; import { anchorPoints, Flyout } from "../DocumentDecorations"; import '../DocumentDecorations.scss'; import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionPDFView } from "./CollectionPDFView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; -import { List } from "../../../new_fields/List"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { Gateway } from "../../northstar/manager/Gateway"; -import { Docs } from "../../documents/Documents"; -import { ContextMenu } from "../ContextMenu"; -import { CollectionView } from "./CollectionView"; -import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch } from "../../util/UndoManager"; +import { CollectionView } from "./CollectionView"; library.add(faCog); @@ -389,6 +386,7 @@ interface CollectionSchemaPreviewProps { CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; removeDocument: (document: Doc) => boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; @@ -424,7 +422,7 @@ export class CollectionSchemaPreview extends React.Component doc) { getTransform={dxf} CollectionView={this.props.CollectionView} addDocument={this.props.addDocument} + moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} active={this.props.active} whenActiveChanged={this.props.whenActiveChanged} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 856fdab7f..8ece7d67f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -285,8 +285,8 @@ export class DocumentView extends DocComponent(Docu let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc)); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); - if (altKey) { - maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"); + if (altKey || ctrlKey) { + maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace")); if (!maxLocation || maxLocation === "inPlace") { let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView); let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e5a43c60a..7a9593a60 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -8,10 +8,10 @@ import { keymap } from "prosemirror-keymap"; import { NodeType } from 'prosemirror-model'; import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { Doc, Opt } from "../../../new_fields/Doc"; +import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { Id } from '../../../new_fields/FieldSymbols'; import { RichTextField } from "../../../new_fields/RichTextField"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; @@ -30,6 +30,8 @@ import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { List } from '../../../new_fields/List'; +import { Templates } from '../Templates'; library.add(faEdit); library.add(faSmile); @@ -139,6 +141,28 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = (url.includes(".mov") || url.includes(".mp4")) ? schema.nodes.video : schema.nodes.image; this._editorView!.dispatch(this._editorView!.state.tr.insert(0, model.create({ src: url }))); e.stopPropagation(); + } else { + if (de.data instanceof DragManager.DocumentDragData) { + let ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc)); + if (!ldocs) { + this.props.Document.subBulletDocs = new List([]); + } + ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc)); + if (!ldocs) return; + if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) { + ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.props.Document.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 })); + this.props.addDocument && this.props.addDocument(ldocs[0] as Doc); + this.props.Document.templates = new List([Templates.Bullet.Layout]); + this.props.Document.isBullet = true; + } + let stackDoc = (ldocs[0] as Doc); + if (de.data.moveDocument) { + de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => { + Cast(stackDoc.data, listSpec(Doc))!.push(doc); + return true; + }) + } + } } } -- cgit v1.2.3-70-g09d2 From 0cd0f5afdef91740fecdedaaecb2ed6be054eee5 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 17 Jun 2019 18:13:36 -0400 Subject: somewhat fixed problem of titles & text boxes --- src/client/views/MainOverlayTextBox.tsx | 4 ++++ src/client/views/Templates.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 23e90ece5..4e983c906 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -10,6 +10,7 @@ import { Transform } from '../util/Transform'; import { CollectionDockingView } from './collections/CollectionDockingView'; import "./MainOverlayTextBox.scss"; import { FormattedTextBox } from './nodes/FormattedTextBox'; +import { For } from 'babel-types'; interface MainOverlayTextBoxProps { } @@ -25,6 +26,7 @@ export class MainOverlayTextBox extends React.Component private _textProxyDiv: React.RefObject; private _textBottom: boolean | undefined; private _textAutoHeight: boolean | undefined; + private _textBox: FormattedTextBox | undefined; @observable public TextDoc?: Doc; constructor(props: MainOverlayTextBoxProps) { @@ -33,6 +35,7 @@ export class MainOverlayTextBox extends React.Component MainOverlayTextBox.Instance = this; reaction(() => FormattedTextBox.InputBoxOverlay, (box?: FormattedTextBox) => { + this._textBox = box; if (box) { this.TextDoc = box.props.Document; let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); @@ -68,6 +71,7 @@ export class MainOverlayTextBox extends React.Component textScroll = (e: React.UIEvent) => { if (this._textProxyDiv.current && this._textTargetDiv) { this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop; + console.log(this._textTargetDiv.scrollTop + " != " + (e as any)._targetInst.stateNode.scrollTop + " != " + (this._textBox!.CurrentDiv ? this._textBox!.CurrentDiv.scrollTop : -1)); } } diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index 3cd525afa..3d5f7b6ea 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -48,12 +48,12 @@ export namespace Templates {
` ); export const Title = new Template("Title", TemplatePosition.InnerTop, - `
+ `
{props.Document.title}
-
-
{layout}
+
+
{layout}
` ); @@ -62,7 +62,7 @@ export namespace Templates {
-
{layout}
+
{layout}
` ); export const Bullet = new Template("Bullet", TemplatePosition.InnerTop, -- cgit v1.2.3-70-g09d2 From df05220570628c0f6f1f97329cd3719bb232ecd2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 17 Jun 2019 19:09:36 -0400 Subject: from last --- src/client/views/nodes/DocumentView.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 7c72fb6e6..3a4b46b7e 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -4,6 +4,7 @@ position: inherit; top: 0; left:0; + pointer-events: all; // background: $light-color; //overflow: hidden; transform-origin: left top; -- cgit v1.2.3-70-g09d2 From 71b1cfbebdc771440c1f3b1169611b5747450064 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 17 Jun 2019 20:09:12 -0400 Subject: changed default color picker --- src/client/views/MainView.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 29015995f..ea49ebd5d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker } from 'react-color'; +import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; import Measure from 'react-measure'; @@ -230,6 +230,14 @@ export class MainView extends React.Component { return { fontSize: "50%" }; } + onColorClick = (e: React.MouseEvent) => { + let target = (e.nativeEvent! as any).path[0]; + let parent = (e.nativeEvent! as any).path[1]; + if (target.localName === "input" || parent.localName === "span") + e.stopPropagation(); + } + + @observable private _colorPickerDisplay = false; /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */ nodesMenu() { @@ -257,8 +265,8 @@ export class MainView extends React.Component {
  • {btns.map(btn => -- cgit v1.2.3-70-g09d2 From 8c64ffd92e382050bc8727981cf9fb830e4f02a7 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 17 Jun 2019 23:04:07 -0400 Subject: Added share with user functionality --- src/client/views/nodes/DocumentView.tsx | 48 +++++++++++++++++++++++++-------- src/client/views/nodes/FieldView.tsx | 2 +- src/server/RouteStore.ts | 1 + src/server/database.ts | 4 +-- src/server/index.ts | 11 ++++++++ 5 files changed, 52 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9d0206e48..942228e71 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,11 +1,11 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; -import { action, computed, IReactionDisposer, reaction, trace, observable } from "mobx"; +import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, FieldValue, StrCast, NumCast, PromiseValue } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { emptyFunction, Utils } from "../../../Utils"; @@ -26,10 +26,12 @@ import { DocComponent } from "../DocComponent"; import { PresentationView } from "../PresentationView"; import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; +import * as rp from "request-promise"; import "./DocumentView.scss"; import React = require("react"); import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { ContextMenuProps } from '../ContextMenuItem'; +import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); @@ -452,7 +454,8 @@ export class DocumentView extends DocComponent(Docu } @action - onContextMenu = (e: React.MouseEvent): void => { + onContextMenu = async (e: React.MouseEvent): Promise => { + e.persist(); e.stopPropagation(); if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 || e.isDefaultPrevented()) { @@ -483,14 +486,37 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); - if (!this.topMost) { - // DocumentViews should stop propagation of this event - e.stopPropagation(); - } - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - if (!SelectionManager.IsSelected(this)) { - SelectionManager.SelectDoc(this, false); - } + type User = { email: string, userDocumentId: string }; + const users: User[] = JSON.parse(await rp.get(DocServer.prepend(RouteStore.getUsers))); + let usersMenu: ContextMenuProps[] = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({ + description: email, event: async () => { + const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc); + if (!userDocument) { + throw new Error(`Couldn't get user document of user ${email}`); + } + const notifDoc = await Cast(userDocument.optionalRightCollection, Doc); + if (notifDoc instanceof Doc) { + const data = await Cast(notifDoc.data, listSpec(Doc)); + const sharedDoc = Doc.MakeAlias(this.props.Document); + if (data) { + data.push(sharedDoc); + } else { + notifDoc.data = new List([sharedDoc]); + } + } + } + })); + runInAction(() => { + cm.addItem({ description: "Share...", subitems: usersMenu }); + if (!this.topMost) { + // DocumentViews should stop propagation of this event + e.stopPropagation(); + } + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + if (!SelectionManager.IsSelected(this)) { + SelectionManager.SelectDoc(this, false); + } + }); } onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index cf6d2012f..4738e90d7 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -109,7 +109,7 @@ export class FieldView extends React.Component { } else if (field instanceof List) { return (
    - {field.map(f => f instanceof Doc ? f.title : f.toString()).join(", ")} + {field.map(f => f instanceof Doc ? f.title : (f && f.toString && f.toString())).join(", ")}
    ); } // bcz: this belongs here, but it doesn't render well so taking it out for now diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index c4af5cdaa..5c13495ff 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -16,6 +16,7 @@ export enum RouteStore { // USER AND WORKSPACES getCurrUser = "/getCurrentUser", + getUsers = "/getUsers", getUserDocumentId = "/getUserDocumentId", updateCursor = "/updateCursor", diff --git a/src/server/database.ts b/src/server/database.ts index 70b3efced..d240bd909 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -120,9 +120,9 @@ export class Database { } } - public query(query: any): Promise { + public query(query: any, collectionName = "newDocuments"): Promise { if (this.db) { - return Promise.resolve(this.db.collection('newDocuments').find(query)); + return Promise.resolve(this.db.collection(collectionName).find(query)); } else { return new Promise(res => { this.onConnect.push(() => res(this.query(query))); diff --git a/src/server/index.ts b/src/server/index.ts index b91c91282..7ef542b01 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -203,6 +203,17 @@ addSecureRoute( RouteStore.root ); +addSecureRoute( + Method.GET, + async (_, res) => { + const cursor = await Database.Instance.query({}, "users"); + const results = await cursor.toArray(); + res.send(results.map(user => ({ email: user.email, userDocumentId: user.userDocumentId }))); + }, + undefined, + RouteStore.getUsers +); + addSecureRoute( Method.GET, (user, res, req) => { -- cgit v1.2.3-70-g09d2 From 4dc8c03562a0473becb895824740da487e16e771 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 00:17:58 -0400 Subject: added dropping of Dash urls from gmail --- src/client/views/collections/CollectionSubView.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index b5a3d087e..af0495e4f 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -182,8 +182,19 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { return; } if (html && html.indexOf(" { + if (f instanceof Doc) { + if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + (f instanceof Doc) && this.props.addDocument(f, false); + } + }); + } else { + let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }); + this.props.addDocument(htmlDoc, false); + } return; } if (text && text.indexOf("www.youtube.com/watch") !== -1) { -- cgit v1.2.3-70-g09d2 From 62c781c0c79ac395c5e117d208a90485ff1ba599 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 02:19:07 -0400 Subject: faster loading of PDFs --- src/client/views/nodes/PDFBox.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 340 +++++++++++++------------------------ 2 files changed, 116 insertions(+), 225 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 655c12ab3..3aaa5749d 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -69,7 +69,6 @@ export class PDFBox extends DocComponent(PdfDocumen loaded = (nw: number, nh: number, np: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - console.log("pages = " + np); doc.numPages = np; if (doc.nativeWidth && doc.nativeHeight) return; let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d74a16f3f..5149e48fd 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -43,16 +43,7 @@ export class PDFViewer extends React.Component { @action componentDidMount() { - const pdfUrl = this.props.url; - console.log("pdf starting to load") - let promise = Pdfjs.getDocument(pdfUrl).promise; - - promise.then((pdf: Pdfjs.PDFDocumentProxy) => { - runInAction(() => { - console.log("pdf url received"); - this._pdf = pdf; - }); - }); + Pdfjs.getDocument(this.props.url).promise.then(pdf => runInAction(() => this._pdf = pdf)); } render() { @@ -83,12 +74,8 @@ class Viewer extends React.Component { // _visibleElements is the array of JSX elements that gets rendered @observable.shallow private _visibleElements: JSX.Element[] = []; // _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder - @observable private _isPage: boolean[] = []; + @observable private _isPage: string[] = []; @observable private _pageSizes: { width: number, height: number }[] = []; - @observable private _startIndex: number = 0; - @observable private _endIndex: number = 1; - @observable private _loaded: boolean = false; - @observable private _pdf: Opt; @observable private _annotations: Doc[] = []; @observable private _pointerEvents: "all" | "none" = "all"; @observable private _savedAnnotations: Dictionary = new Dictionary(); @@ -97,7 +84,6 @@ class Viewer extends React.Component { private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; - private _pagesLoaded: number = 0; private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: IViewerProps) { @@ -106,77 +92,62 @@ class Viewer extends React.Component { this._annotationLayer = React.createRef(); } + componentDidUpdate = (prevProps: IViewerProps) => { + if (this.scrollY !== prevProps.scrollY && this._visibleElements.length) { + this.renderPages(this.startIndex, this.endIndex, false); + } + } + @action componentDidMount = () => { let wasSelected = this.props.parent.props.active(); - // reaction for when document gets (de)selected this._reactionDisposer = reaction( - () => [this.props.parent.props.active(), this.startIndex], - () => { - // if deselected, render images in place of pdf - if (wasSelected && !this.props.parent.props.active()) { - this.saveThumbnail(); - } - // if selected, render pdf - else if (!wasSelected && this.props.parent.props.active()) { - this.renderPages(this.startIndex, this.endIndex, true); - } + () => [this.props.parent.props.active(), this.startIndex, this.props.pdf], + async () => { + await this.initialLoad(); wasSelected = this.props.parent.props.active(); - this._pointerEvents = wasSelected ? "none" : "all"; - }, - { fireImmediately: true } - ); + runInAction(() => this._pointerEvents = wasSelected ? "none" : "all"); + this.renderPages(this.startIndex, this.endIndex, false); + }, { fireImmediately: true }); - if (this.props.parent.Document) { - this._annotationReactionDisposer = reaction( - () => DocListCast(this.props.parent.Document.annotations), - () => { - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - this.renderAnnotations(annotations, true); - } - }, - { fireImmediately: true } - ); - } + this._annotationReactionDisposer = reaction( + () => this.props.parent.Document && DocListCast(this.props.parent.Document.annotations), + (annotations: Doc[]) => + annotations && annotations.length && this.renderAnnotations(annotations, true), + { fireImmediately: true }); + } - setTimeout(() => { - // this.renderPages(this.startIndex, this.endIndex, true); - this.initialLoad(); - }, 1000); + componentWillUnmount = () => { + this._reactionDisposer && this._reactionDisposer(); + this._annotationReactionDisposer && this._annotationReactionDisposer(); } @action - initialLoad = () => { - let pdf = this.props.pdf; - if (pdf) { - this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages); - let rendered = 0; - for (let i = 0; i < pdf.numPages; i++) { - pdf.getPage(i + 1).then( - (page: Pdfjs.PDFPageProxy) => { - runInAction(() => { - this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }; - }); - console.log(`page ${i} size retreieved`); - rendered++; - if (rendered === pdf!.numPages - 1) { - this.saveThumbnail(); - } - } - ); + initialLoad = async () => { + if (this.props.pdf && this._pageSizes.length === 0) { + let pageSizes = Array<{ width: number, height: number }>(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 }; + if (i === 0) this.props.loaded(pageSizes[i].width, pageSizes[i].height, this.props.pdf!.numPages); + })); } + runInAction(() => { + this._pageSizes = pageSizes; + let divs = Array.from(Array(this._pageSizes.length).keys()).map(i => ( +
    + )); + this._isPage = Array.from(Array(this._pageSizes.length).map(p => "none")); + this._visibleElements = new Array(...divs); + }) } } private mainCont = (div: HTMLDivElement | null) => { - if (this._dropDisposer) { - this._dropDisposer(); - } + this._dropDisposer && this._dropDisposer(); if (div) { - this._dropDisposer = DragManager.MakeDropTarget(div, { - handlers: { drop: this.drop.bind(this) } - }); + this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } }); } } @@ -222,154 +193,102 @@ class Viewer extends React.Component { e.stopPropagation(); } } - - componentWillUnmount = () => { - if (this._reactionDisposer) { - this._reactionDisposer(); - } - if (this._annotationReactionDisposer) { - this._annotationReactionDisposer(); - } - } - + /** + * Called by the Page class when it gets rendered, initializes the lists and + * puts a placeholder with all of the correct page sizes when all of the pages have been loaded. + */ @action - saveThumbnail = async () => { - // file address of the pdf - const address: string = this.props.url; - for (let i = 0; i < this._visibleElements.length; i++) { - if (this._isPage[i]) { - // change the address to be the file address of the PNG version of each page - let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`))); - let thisAddress = res.path; - let nWidth = parseInt(res.width); - let nHeight = parseInt(res.height); - // replace page with image - runInAction(() => - this._visibleElements[i] = ); - } - } - } - - @computed get scrollY(): number { - return this.props.scrollY; - } - - @computed get startIndex(): number { - return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer); - } - - @computed get endIndex(): number { - let width = this._pageSizes.map(i => i ? i.width : 0); - return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); - } - - componentDidUpdate = (prevProps: IViewerProps) => { - if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) { - this._pdf = this.props.pdf; - // render pages if the scorll position changes - console.log(`START: ${this.startIndex}, END: ${this.endIndex}`); - this.renderPages(this.startIndex, this.endIndex); - } + pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { + this.props.pdf && this.props.loaded && this.props.loaded(page.width, page.height, this.props.pdf.numPages); } - @action - private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { - if (removeOldAnnotations) { - this._annotations = annotations; - } - else { - this._annotations.push(...annotations); - this._annotations = new Array(...this._annotations); + getPlaceholderPage = (page: number) => { + if (this._isPage[page] !== "none") { + this._isPage[page] = "none"; + this._visibleElements[page] = ( +
    + ); } } - - /** - * @param startIndex: where to start rendering pages - * @param endIndex: where to end rendering pages - * @param forceRender: (optional), force pdfs to re-render, even if the page already exists - */ @action - renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - if (!this.props.pdf) { - return; - } - - if (this._pageSizes.length !== numPages) { - this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 })); - } - - // this is only for an initial render to get all of the pages rendered - if (this._visibleElements.length !== numPages) { - let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? ( + getRenderedPage = (page: number) => { + if (this._isPage[page] !== "page") { + this._isPage[page] = "page"; + this._visibleElements[page] = ( - ) : - (
    ) ); - let arr = Array.from(Array(numPages).keys()).map(i => i < 5); - this._visibleElements.push(...divs); - this._isPage.push(...arr); } + } - // if nothing changed, return - if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) { - return; + // change the address to be the file address of the PNG version of each page + // file address of the pdf + @action + getPageImage = async (page: number) => { + let handleError = () => this.getRenderedPage(page); + if (this._isPage[page] != "image") { + this._isPage[page] = "image"; + const address = this.props.url; + let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); + runInAction(() => this._visibleElements[page] = ); } + } - // unrender pages outside of the pdf by replacing them with empty stand-in divs - for (let i = 0; i < numPages; i++) { - if (i < startIndex || i > endIndex) { - if (this._isPage[i]) { - this._visibleElements[i] = ( -
    - ); + @computed get scrollY(): number { return this.props.scrollY; } + + @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); } + + @computed get endIndex(): number { + let width = this._pageSizes.map(i => i ? i.width : 0); + return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer); + } + + /** + * @param startIndex: where to start rendering pages + * @param endIndex: where to end rendering pages + * @param forceRender: (optional), force pdfs to re-render, even if the page already exists + */ + @action + renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { + if (this.props.pdf) { + // unrender pages outside of the pdf by replacing them with empty stand-in divs + for (let i = 0; i < this.props.pdf.numPages; i++) { + if (i < startIndex || i > endIndex) { + this.getPlaceholderPage(i); + } else { + if (this.props.parent.props.active()) { + this.getRenderedPage(i); + } else { + this.getPageImage(i); + } } - this._isPage[i] = false; } } + } - // render pages for any indices that don't already have pages (force rerender will make these render regardless) - for (let i = startIndex; i <= endIndex; i++) { - if (!this._isPage[i] || (this._isPage[i] && forceRender)) { - this._visibleElements[i] = ( - - ); - this._isPage[i] = true; - } + @action + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); } - - this._startIndex = startIndex; - this._endIndex = endIndex; - - return; } @action @@ -392,7 +311,7 @@ class Viewer extends React.Component { let pinAnno = new Doc(); pinAnno.x = x; - pinAnno.y = y + this.getPageHeight(page); + pinAnno.y = y + this.getScrollFromPage(page); pinAnno.width = pinAnno.height = PinRadius; pinAnno.page = page; pinAnno.target = targetDoc; @@ -411,9 +330,7 @@ class Viewer extends React.Component { } // get the page index that the vertical offset passed in is on - getIndex = (vOffset: number) => { - // if (this._loaded) { - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; + getPageFromScroll = (vOffset: number) => { let index = 0; let currOffset = vOffset; while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { @@ -421,34 +338,10 @@ class Viewer extends React.Component { index++; } return index; - // } - return 0; } - /** - * Called by the Page class when it gets rendered, initializes the lists and - * puts a placeholder with all of the correct page sizes when all of the pages have been loaded. - */ - @action - pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { - if (this._loaded) { - return; - } - let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.props.loaded(page.width, page.height, numPages); - this._pageSizes[index - 1] = { width: page.width, height: page.height }; - this._pagesLoaded++; - if (this._pagesLoaded === numPages) { - this._loaded = true; - let divs = Array.from(Array(numPages).keys()).map(i => ( -
    - )); - this._visibleElements = new Array(...divs); - this.renderPages(this.startIndex, this.endIndex, true); - } - } - getPageHeight = (index: number): number => { + getScrollFromPage = (index: number): number => { let counter = 0; if (this.props.pdf && index < this.props.pdf.numPages) { for (let i = 0; i < index; i++) { @@ -463,7 +356,7 @@ class Viewer extends React.Component { createAnnotation = (div: HTMLDivElement, page: number) => { if (this._annotationLayer.current) { if (div.style.top) { - div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString(); + div.style.top = (parseInt(div.style.top) + this.getScrollFromPage(page)).toString(); } this._annotationLayer.current.append(div); let savedPage = this._savedAnnotations.getValue(page); @@ -494,7 +387,6 @@ class Viewer extends React.Component { } render() { - trace(); return (
    -- cgit v1.2.3-70-g09d2 From 2f5c38c6a0a5220c2a31931c34d94e199854d703 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 08:36:37 -0400 Subject: more streamlining --- src/client/views/pdf/PDFViewer.tsx | 144 +++++++++++++++---------------------- 1 file changed, 57 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5149e48fd..645afc636 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,29 +1,23 @@ +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import React = require("react"); -import { observable, action, runInAction, computed, IReactionDisposer, reaction, trace } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc"; -import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; -import { PDFBox } from "../nodes/PDFBox"; -import Page from "./Page"; -import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types"; +import * as rp from "request-promise"; +import { Dictionary } from "typescript-collections"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import { DocUtils, Docs } from "../../documents/Documents"; -import { DocumentManager } from "../../util/DocumentManager"; -import { SelectionManager } from "../../util/SelectionManager"; import { List } from "../../../new_fields/List"; -import { DocumentContentsView } from "../nodes/DocumentContentsView"; -import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; -import { Transform } from "../../util/Transform"; -import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; -import { DocumentView } from "../nodes/DocumentView"; -import { DragManager } from "../../util/DragManager"; -import { Dictionary } from "typescript-collections"; -import * as rp from "request-promise"; -import { restProperty } from "babel-types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { DocServer } from "../../DocServer"; -import { number } from "prop-types"; +import { Docs, DocUtils } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { DragManager } from "../../util/DragManager"; +import { DocumentView } from "../nodes/DocumentView"; +import { PDFBox } from "../nodes/PDFBox"; +import Page from "./Page"; +import "./PDFViewer.scss"; +import React = require("react"); export const scale = 2; interface IPDFViewerProps { @@ -49,14 +43,15 @@ export class PDFViewer extends React.Component { render() { return (
    - + {!this._pdf ? (null) : + }
    ); } } interface IViewerProps { - pdf: Opt; + pdf: Pdfjs.PDFDocumentProxy; loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; @@ -77,37 +72,27 @@ class Viewer extends React.Component { @observable private _isPage: string[] = []; @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; - @observable private _pointerEvents: "all" | "none" = "all"; @observable private _savedAnnotations: Dictionary = new Dictionary(); private _pageBuffer: number = 1; - private _annotationLayer: React.RefObject; + private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _dropDisposer?: DragManager.DragDropDisposer; - constructor(props: IViewerProps) { - super(props); - - this._annotationLayer = React.createRef(); - } - componentDidUpdate = (prevProps: IViewerProps) => { - if (this.scrollY !== prevProps.scrollY && this._visibleElements.length) { - this.renderPages(this.startIndex, this.endIndex, false); + if (this.scrollY !== prevProps.scrollY) { + this.renderPages(); } } @action componentDidMount = () => { - let wasSelected = this.props.parent.props.active(); this._reactionDisposer = reaction( - () => [this.props.parent.props.active(), this.startIndex, this.props.pdf], + () => [this.props.parent.props.active(), this.startIndex, this.endIndex], async () => { await this.initialLoad(); - wasSelected = this.props.parent.props.active(); - runInAction(() => this._pointerEvents = wasSelected ? "none" : "all"); - this.renderPages(this.startIndex, this.endIndex, false); + this.renderPages(); }, { fireImmediately: true }); this._annotationReactionDisposer = reaction( @@ -124,23 +109,15 @@ class Viewer extends React.Component { @action initialLoad = async () => { - if (this.props.pdf && this._pageSizes.length === 0) { + if (this._pageSizes.length === 0) { let pageSizes = Array<{ width: number, height: number }>(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 }; - if (i === 0) this.props.loaded(pageSizes[i].width, pageSizes[i].height, this.props.pdf!.numPages); - })); + await this.props.pdf.getPage(i + 1).then(page => runInAction(() => + pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale })); } - runInAction(() => { - this._pageSizes = pageSizes; - let divs = Array.from(Array(this._pageSizes.length).keys()).map(i => ( -
    - )); - this._isPage = Array.from(Array(this._pageSizes.length).map(p => "none")); - this._visibleElements = new Array(...divs); - }) + this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages); + runInAction(() => + Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage)) } } @@ -199,14 +176,15 @@ class Viewer extends React.Component { */ @action pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { - this.props.pdf && this.props.loaded && this.props.loaded(page.width, page.height, this.props.pdf.numPages); + this.props.loaded(page.width, page.height, this.props.pdf.numPages); } @action getPlaceholderPage = (page: number) => { if (this._isPage[page] !== "none") { this._isPage[page] = "none"; this._visibleElements[page] = ( -
    +
    ); } } @@ -219,8 +197,8 @@ class Viewer extends React.Component { pdf={this.props.pdf} page={page} numPages={this.props.pdf!.numPages} - key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${page + 1}` : "undefined"}`} - name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${page + 1}` : "undefined"}`} + key={`rendered-${page + 1}`} + name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`} pageLoaded={this.pageLoaded} parent={this.props.parent} makePin={this.createPinAnnotation} @@ -243,38 +221,33 @@ class Viewer extends React.Component { this._isPage[page] = "image"; const address = this.props.url; let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); - runInAction(() => this._visibleElements[page] = ); + runInAction(() => this._visibleElements[page] = + ); } } @computed get scrollY(): number { return this.props.scrollY; } + // startIndex: where to start rendering pages @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); } + // endIndex: where to end rendering pages @computed get endIndex(): number { - let width = this._pageSizes.map(i => i ? i.width : 0); - return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer); + let width = this._pageSizes.map(i => i.width); + return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer); } - /** - * @param startIndex: where to start rendering pages - * @param endIndex: where to end rendering pages - * @param forceRender: (optional), force pdfs to re-render, even if the page already exists - */ @action - renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => { - if (this.props.pdf) { - // unrender pages outside of the pdf by replacing them with empty stand-in divs - for (let i = 0; i < this.props.pdf.numPages; i++) { - if (i < startIndex || i > endIndex) { - this.getPlaceholderPage(i); + renderPages = () => { + for (let i = 0; i < this.props.pdf.numPages; i++) { + if (i < this.startIndex || i > this.endIndex) { + this.getPlaceholderPage(i); // pages outside of the pdf use empty stand-in divs + } else { + if (this.props.parent.props.active()) { + this.getRenderedPage(i); } else { - if (this.props.parent.props.active()) { - this.getRenderedPage(i); - } else { - this.getPageImage(i); - } + this.getPageImage(i); } } } @@ -308,7 +281,6 @@ class Viewer extends React.Component { 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); @@ -333,22 +305,16 @@ class Viewer extends React.Component { getPageFromScroll = (vOffset: number) => { let index = 0; let currOffset = vOffset; - while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { - currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; - index++; + while (index < this._pageSizes.length && currOffset - this._pageSizes[index].height > 0) { + currOffset -= this._pageSizes[index++].height; } return index; } - getScrollFromPage = (index: number): number => { let counter = 0; - if (this.props.pdf && index < this.props.pdf.numPages) { - for (let i = 0; i < index; i++) { - if (this._pageSizes[i]) { - counter += this._pageSizes[i].height; - } - } + for (let i = 0; i < Math.min(this.props.pdf.numPages, index); i++) { + counter += this._pageSizes[i].height; } return counter; } @@ -392,7 +358,11 @@ class Viewer extends React.Component {
    {this._visibleElements}
    -
    +
    {this._annotations.map(anno => this.renderAnnotation(anno))}
    -- cgit v1.2.3-70-g09d2 From a3b8a57027d7c45ea19d259e1ec18fa6a8648c24 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 08:49:02 -0400 Subject: looked like wrong code... --- src/client/views/pdf/PDFViewer.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 645afc636..e5fb32e53 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -234,8 +234,7 @@ class Viewer extends React.Component { // endIndex: where to end rendering pages @computed get endIndex(): number { - let width = this._pageSizes.map(i => i.width); - return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + Math.max(...width)) + this._pageBuffer); + return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer); } @action -- cgit v1.2.3-70-g09d2 From 4b8324fcf44c5d3c3a4b3f6e98a4d1dfce84811b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 08:53:01 -0400 Subject: removed trace --- src/client/views/nodes/PDFBox.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3aaa5749d..e79182e9b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -96,7 +96,6 @@ export class PDFBox extends DocComponent(PdfDocumen } render() { - trace(); // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); console.log(pdfUrl); -- cgit v1.2.3-70-g09d2 From 64e6a941639aab8d7109178aa151a50909547309 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 09:05:41 -0400 Subject: fixed index out of range --- src/client/views/nodes/PDFBox.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e79182e9b..38e91ac12 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -98,7 +98,6 @@ export class PDFBox extends DocComponent(PdfDocumen render() { // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); - console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
    { initialLoad = async () => { if (this._pageSizes.length === 0) { let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); + this._isPage = Array(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 })); } - this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages); runInAction(() => - Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage)) + Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage)); + this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages); } } -- cgit v1.2.3-70-g09d2 From cc032e2f60015728f64f46ef009c9306e36746a0 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 18 Jun 2019 10:05:49 -0400 Subject: fixes --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 24 +++++++++++++++++------- src/client/views/pdf/PDFViewer.tsx | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 655c12ab3..ae68a530e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -62,7 +62,7 @@ export class PDFBox extends DocComponent(PdfDocumen createRef = (ele: HTMLDivElement | null) => { if (this._reactionDisposer) this._reactionDisposer(); this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => { - ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "auto" }); }); } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index b44370e3d..7817e8c26 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -10,8 +10,8 @@ import { Doc } from "../../../new_fields/Doc"; export default class PDFMenu extends React.Component { static Instance: PDFMenu; - @observable private _top: number = 0; - @observable private _left: number = 0; + @observable private _top: number = -300; + @observable private _left: number = -300; @observable private _opacity: number = 1; @observable private _transition: string = "opacity 0.5s"; @observable private _transitionDelay: string = ""; @@ -22,18 +22,25 @@ export default class PDFMenu extends React.Component { @observable Highlighting: boolean = false; private _timeout: NodeJS.Timeout | undefined; + private _offsetY: number = 0; + private _offsetX: number = 0; + private _mainCont: React.RefObject; constructor(props: Readonly<{}>) { super(props); PDFMenu.Instance = this; + + this._mainCont = React.createRef(); } pointerDown = (e: React.PointerEvent) => { document.removeEventListener("pointermove", this.StartDrag); document.addEventListener("pointermove", this.StartDrag); - document.removeEventListener("pointerup", this.pointerUp) - document.addEventListener("pointerup", this.pointerUp) + document.removeEventListener("pointerup", this.pointerUp); + document.addEventListener("pointerup", this.pointerUp); + + console.log(this.StartDrag); e.stopPropagation(); e.preventDefault(); @@ -102,8 +109,8 @@ export default class PDFMenu extends React.Component { @action dragging = (e: PointerEvent) => { - this._left += e.movementX; - this._top += e.movementY; + this._left = e.pageX - this._offsetX; + this._top = e.pageY - this._offsetY; e.stopPropagation(); e.preventDefault(); @@ -122,6 +129,9 @@ export default class PDFMenu extends React.Component { document.removeEventListener("pointerup", this.dragEnd); document.addEventListener("pointerup", this.dragEnd); + this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX; + this._offsetY = e.nativeEvent.offsetY; + e.stopPropagation(); e.preventDefault(); } @@ -139,7 +149,7 @@ export default class PDFMenu extends React.Component { render() { return ( -
    ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index af0495e4f..783f40d0a 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -181,21 +181,28 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } return; } - if (html && html.indexOf(" { - if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f, false); - } - }); + if (html && !html.startsWith(" { + if (f instanceof Doc) { + if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + (f instanceof Doc) && this.props.addDocument(f, false); + } + }); + } else { + let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }); + this.props.addDocument(htmlDoc, false); + } + return; } - return; } if (text && text.indexOf("www.youtube.com/watch") !== -1) { const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/"); -- cgit v1.2.3-70-g09d2 From 66d4cc94bcc69f590d90dd35823f93b8e2fb90d8 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 18 Jun 2019 10:52:10 -0400 Subject: selection fixes --- src/client/views/nodes/PDFBox.tsx | 5 +++-- src/client/views/pdf/PDFViewer.tsx | 6 +++--- src/client/views/pdf/Page.tsx | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 80d274c6d..7dd2b1dc5 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -87,7 +87,6 @@ export class PDFBox extends DocComponent(PdfDocumen onScroll = (e: React.UIEvent) => { if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; - // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.scrollY = this._scrollY; @@ -107,7 +106,9 @@ export class PDFBox extends DocComponent(PdfDocumen marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} ref={this.createRef} - onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> + onWheel={(e: React.WheelEvent) => { + e.stopPropagation(); + }} className={classname}> {/*
    */}
    diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 86a17c0a6..69372f43b 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -388,7 +388,7 @@ class Viewer extends React.Component { {this._annotations.map(anno => this.renderAnnotation(anno))}
    -
    +
    ); } } @@ -522,7 +522,7 @@ class PinAnnotation extends React.Component { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - onPointerDown = (e: React.PointerEvent) => { + onPointerDown = (e: React.MouseEvent) => { let targetDoc = Cast(this.props.document.target, Doc, null); if (targetDoc) { DocumentManager.Instance.jumpToDocument(targetDoc); @@ -531,7 +531,7 @@ class RegionAnnotation extends React.Component { render() { return ( -
    ); } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bb87ec9d4..a19b64eda 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -402,7 +402,8 @@ export default class Page extends React.Component { let boundingRect = this._textLayer.current.getBoundingClientRect(); for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); - if (rect) { + if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) { + console.log(rect); let annoBox = document.createElement("div"); annoBox.className = "pdfViewer-annotationBox"; // transforms the positions from screen onto the pdf div -- cgit v1.2.3-70-g09d2 From 749eef13af1338225b2bec4dbcd7a50a5650d285 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 11:31:46 -0400 Subject: fixed image drag drop when not selected. --- src/client/views/nodes/DocumentView.tsx | 7 +++++-- src/client/views/nodes/ImageBox.tsx | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 942228e71..de6e9f5fa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -48,6 +48,8 @@ library.add(faLink); library.add(faFingerprint); library.add(faCrosshairs); library.add(faDesktop); +library.add(faUnlock); +library.add(faLock); const linkSchema = createSchema({ title: "string", @@ -354,7 +356,7 @@ export class DocumentView extends DocComponent(Docu if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - if (!e.altKey && !this.topMost && e.buttons === 1) { + if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) { this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander); } } @@ -475,6 +477,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Open...", subitems: subitems }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); + cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: () => this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); cm.addItem({ description: "Find aliases", event: async () => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0d19508fa..6b8b64c5f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -86,9 +86,9 @@ export class ImageBox extends DocComponent(ImageD } onPointerDown = (e: React.PointerEvent): void => { - if (e.shiftKey && e.ctrlKey) - - e.stopPropagation(); + if (e.shiftKey && e.ctrlKey) { + e.stopPropagation(); // allows default system drag drop of images with shift+ctrl only + } else e.preventDefault(); // if (Date.now() - this._lastTap < 300) { // if (e.buttons === 1) { // this._downX = e.clientX; -- cgit v1.2.3-70-g09d2 From f4fcf306e2579b7479610899a01c06fb157d47de Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 12:03:14 -0400 Subject: fixed goldenlayout nesting --- .../views/collections/CollectionDockingView.tsx | 4 +- src/client/views/nodes/FieldView.tsx | 43 +++++++++++----------- src/client/views/nodes/KeyValuePair.tsx | 2 +- 3 files changed, 24 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index b5d57a015..4d48cedd8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -48,9 +48,7 @@ export class CollectionDockingView extends React.Component { return

    {field.date.toLocaleString()}

    ; } else if (field instanceof Doc) { - let returnHundred = () => 100; - return ( - - ); + return

    {field.title}

    ; + // let returnHundred = () => 100; + // return ( + // + // ); } else if (field instanceof List) { return (
    diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index e8bc17532..dd1bca7f6 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -40,7 +40,7 @@ export class KeyValuePair extends React.Component { focus: emptyFunction, PanelWidth: returnZero, PanelHeight: returnZero, - addDocTab: emptyFunction + addDocTab: returnZero, }; let contents = ; let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")"; -- cgit v1.2.3-70-g09d2 From 3a25bad918c72f5d6de9a720de9e0d316c00f2fe Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 13:03:28 -0400 Subject: fixed issues with expanding text boxes that have a dynamic title --- src/client/views/MainOverlayTextBox.tsx | 1 - src/client/views/collections/CollectionSubView.tsx | 8 +------- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 4e983c906..0de880175 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -71,7 +71,6 @@ export class MainOverlayTextBox extends React.Component textScroll = (e: React.UIEvent) => { if (this._textProxyDiv.current && this._textTargetDiv) { this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop; - console.log(this._textTargetDiv.scrollTop + " != " + (e as any)._targetInst.stateNode.scrollTop + " != " + (this._textBox!.CurrentDiv ? this._textBox!.CurrentDiv.scrollTop : -1)); } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 783f40d0a..e55cd9e37 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -36,9 +36,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; protected createDropTarget = (ele: HTMLDivElement) => { - if (this.dropDisposer) { - this.dropDisposer(); - } + this.dropDisposer && this.dropDisposer(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } @@ -96,10 +94,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { return added; } else if (de.data instanceof DragManager.AnnotationDragData) { - console.log("dropped!"); - console.log(de.data); - // de.data.dropDocument.x = de.x; - // de.data.dropDocument.y = de.y; return this.props.addDocument(de.data.dropDocument); } return false; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 659cb2f28..b13694e9d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -314,7 +314,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { this.onDrop(e, {}); } render() { - let dropAction = Cast(this.props.Document.dropAction, "string") as dropActionType; + let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; if (!this.childDocs) { return (null); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index aa44995ca..36d902c4f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEdit, faSmile } from '@fortawesome/free-solid-svg-icons'; -import { action, IReactionDisposer, observable, reaction } from "mobx"; +import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; @@ -196,7 +196,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe FormattedTextBox.InputBoxOverlay = this; FormattedTextBox.InputBoxOverlayScroll = this._ref.current!.scrollTop; } - }); + }, { fireImmediately: true }); } this._reactionDisposer = reaction( -- cgit v1.2.3-70-g09d2 From 2633f61d311528e62d50d4ff56f5884b3b51ac61 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 13:12:15 -0400 Subject: added undo/redo bindings for app. --- src/client/views/MainView.tsx | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ea49ebd5d..fd76cbbd3 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -105,6 +105,12 @@ export class MainView extends React.Component { if (e.key === "Escape") { DragManager.AbortDrag(); SelectionManager.DeselectAll(); + } else if (e.key === "z" && e.ctrlKey) { + e.preventDefault(); + UndoManager.Undo(); + } else if ((e.key === "y" && e.ctrlKey) || (e.key === "z" && e.ctrlKey && e.shiftKey)) { + e.preventDefault(); + UndoManager.Redo(); } }, false); // drag event handler // click interactions for the context menu -- cgit v1.2.3-70-g09d2 From 9544576ec0167d64f564ae4c87d392eba07ff467 Mon Sep 17 00:00:00 2001 From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> Date: Tue, 18 Jun 2019 13:18:34 -0400 Subject: Added tab focusing on hover --- src/client/views/collections/CollectionDockingView.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 4d48cedd8..9aa4ab45a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -336,6 +336,15 @@ export class CollectionDockingView extends React.Component { + if (!this._isPointerDown) return; + var activeContentItem = tab.header.parent.getActiveContentItem(); + if (tab.contentItem !== activeContentItem) { + tab.header.parent.setActiveContentItem(tab.contentItem); + } + tab.setActive(true); + } ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); @@ -503,4 +512,4 @@ export class DockedFrameRenderer extends React.Component { {({ measureRef }) =>
    {theContent}
    } ; } -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From b1af251b058743798aa3fa3895d22327c8560dfc Mon Sep 17 00:00:00 2001 From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> Date: Tue, 18 Jun 2019 13:19:50 -0400 Subject: Added pointer down flag for tab focus --- src/client/views/collections/CollectionDockingView.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9aa4ab45a..6b0e6deba 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -45,6 +45,7 @@ export class CollectionDockingView extends React.Component(); private _flush: boolean = false; private _ignoreStateChange = ""; + private _isPointerDown = false; constructor(props: SubCollectionViewProps) { super(props); -- cgit v1.2.3-70-g09d2 From 04668e21313f6e62e5ab35ac737fc54191769a5a Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 18 Jun 2019 13:30:41 -0400 Subject: fixed cleanup of marquee keyhandler. --- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 5f2a732b9..d3d4a8130 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -50,7 +50,7 @@ export class MarqueeView extends React.Component document.removeEventListener("pointerup", this.onPointerUp, true); document.removeEventListener("pointermove", this.onPointerMove, true); } - if (rem_keydown) { + if (all) { document.removeEventListener("keydown", this.marqueeCommand, true); } this._visible = false; -- cgit v1.2.3-70-g09d2 From b0ac30172019713e1c75083c1199485d902e0eed Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 16:37:28 -0400 Subject: Fixed zoomBasis stuff and added deletion handling for reponse from server --- .../views/collections/collectionFreeForm/MarqueeView.tsx | 6 ++---- src/new_fields/Doc.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d3d4a8130..dedcc3172 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -275,13 +275,11 @@ export class MarqueeView extends React.Component panY: 0, borderRounding: e.key === "e" ? -1 : undefined, backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", - scale: zoomBasis, - width: bounds.width * zoomBasis, - height: bounds.height * zoomBasis, + width: bounds.width, + height: bounds.height, ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined, title: e.key === "s" || e.key === "S" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection", }); - newCollection.zoomBasis = zoomBasis; this.marqueeInkDelete(inkData); if (e.key === "s") { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c2cfda079..1b0ff812f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -132,6 +132,16 @@ export class Doc extends RefField { this[fKey] = value; } } + const unset = diff.$unset; + if (unset) { + for (const key in unset) { + if (!key.startsWith("fields.")) { + continue; + } + const fKey = key.substring(7); + delete this[fKey]; + } + } } } -- cgit v1.2.3-70-g09d2 From ca126adda9e4def83fb5c2e07e382917ca0b4ee0 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 17:24:59 -0400 Subject: Fixed docking view? --- src/client/views/collections/CollectionDockingView.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6b0e6deba..f2b3528b8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -250,6 +250,7 @@ export class CollectionDockingView extends React.Component { + this._isPointerDown = false; if (this._flush) { this._flush = false; setTimeout(() => this.stateChanged(), 10); @@ -257,6 +258,7 @@ export class CollectionDockingView extends React.Component { + this._isPointerDown = true; var className = (e.target as any).className; if (className === "messageCounter") { e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 464fa03d6ebb2a7aaef1d7622afa3e1e7ee816a3 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 20:11:31 -0400 Subject: Context menu improvements and error fixes --- src/client/views/ContextMenu.scss | 22 +++++----- src/client/views/ContextMenuItem.tsx | 47 +++++++++++++++++++--- .../views/collections/CollectionSchemaView.tsx | 1 + src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 5 ++- 5 files changed, 57 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 7e066d53a..e363c5158 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -8,19 +8,19 @@ flex-direction: column; } -.contextMenu-item:first-child { - background: $intermediate-color; - color: $light-color; -} +// .contextMenu-item:first-child { +// background: $intermediate-color; +// color: $light-color; +// } -.contextMenu-item:first-child::placeholder { - color: $light-color; -} +// .contextMenu-item:first-child::placeholder { +// color: $light-color; +// } -.contextMenu-item:first-child:hover { - background: $intermediate-color; - color: $light-color; -} +// .contextMenu-item:first-child:hover { +// background: $intermediate-color; +// color: $light-color; +// } .contextMenu-subMenu-cont { position: absolute; diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index fcda0db89..dc0751049 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -1,9 +1,12 @@ import React = require("react"); import { observable, action } from "mobx"; import { observer } from "mobx-react"; -import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +library.add(faAngleRight); + export interface OriginalMenuProps { description: string; event: (e: React.MouseEvent) => void; @@ -14,6 +17,7 @@ export interface OriginalMenuProps { export interface SubmenuProps { description: string; subitems: ContextMenuProps[]; + icon?: IconProp; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -41,13 +45,40 @@ export class ContextMenuItem extends React.Component { } } + currentTimeout?: any; + static readonly timeout = 300; + onPointerEnter = () => { + if (this.currentTimeout) { + clearTimeout(this.currentTimeout); + this.currentTimeout = undefined; + } + if (this.overItem) { + return; + } + this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout); + } + + onPointerLeave = () => { + if (this.currentTimeout) { + clearTimeout(this.currentTimeout); + this.currentTimeout = undefined; + } + if (!this.overItem) { + return; + } + this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout); + + } + render() { if ("event" in this.props) { return (
    - - {this.props.icon ? : } - + {this.props.icon ? ( + + + + ) : null}
    {this.props.description}
    @@ -60,9 +91,15 @@ export class ContextMenuItem extends React.Component { {this._items.map(prop => )}
    ; return ( -
    { this.overItem = true; })} onMouseLeave={action(() => this.overItem = false)}> +
    + {this.props.icon ? ( + + + + ) : null}
    {this.props.description} +
    {submenu}
    diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 4b46c73c1..14a7d19d0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -352,6 +352,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { height={this.previewHeight} getTransform={this.getPreviewTransform} CollectionView={this.props.CollectionView} + moveDocument={this.props.moveDocument} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} active={this.props.active} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index c2caabb92..02396c3af 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -23,7 +23,6 @@ import { FieldViewProps } from "./FieldView"; import { Without, OmitKeys } from "../../../Utils"; import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; -import { PDFBox2 } from "../pdf/PDFBox2"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index de6e9f5fa..7c058d91c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faShare, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -35,6 +35,7 @@ import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); +library.add(faShare); library.add(faExpandArrowsAlt); library.add(faCompressArrowsAlt); library.add(faLayerGroup); @@ -510,7 +511,7 @@ export class DocumentView extends DocComponent(Docu } })); runInAction(() => { - cm.addItem({ description: "Share...", subitems: usersMenu }); + cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" }); if (!this.topMost) { // DocumentViews should stop propagation of this event e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 18 Jun 2019 20:58:32 -0400 Subject: Fixed linter errors --- src/client/northstar/dash-nodes/HistogramBox.tsx | 3 +-- src/client/util/DocumentManager.ts | 6 +++--- src/client/util/DragManager.ts | 4 +++- src/client/util/RichTextSchema.tsx | 10 +++++----- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/ContextMenu.tsx | 4 ++-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 7 ++++--- src/client/views/PresentationView.tsx | 4 ++-- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 5 +++-- src/client/views/collections/CollectionPDFView.tsx | 2 +- src/client/views/collections/CollectionSchemaView.tsx | 11 ++++++----- src/client/views/collections/CollectionStackingView.tsx | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 10 +++++----- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 12 ++++++------ src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 3 ++- src/client/views/pdf/PDFAnnotationLayer.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 4 ++-- src/server/index.ts | 2 +- 27 files changed, 59 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index d7732ee86..a60eaea85 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -125,8 +125,7 @@ export class HistogramBox extends React.Component { let mapped = brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; let brushed = DocListCast(brush.brushingDocs); - if (!brushed.length) - return null; + if (!brushed.length) return null; return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; }); runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[])); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ff0c1560b..862395d74 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -35,9 +35,9 @@ export class DocumentManager { let toReturn: DocumentView | null = null; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; - for (let i = 0; i < passes.length; i++) { + for (let pass of passes) { DocumentManager.Instance.DocumentViews.map(view => { - if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { + if (view.props.Document[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; return; } @@ -45,7 +45,7 @@ export class DocumentManager { if (!toReturn) { DocumentManager.Instance.DocumentViews.map(view => { let doc = view.props.Document.proto; - if (doc && doc[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { + if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; } }); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 89566e777..c3c92daa5 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -282,11 +282,13 @@ export namespace DragManager { // } let set = dragElement.getElementsByTagName('*'); if (dragElement.hasAttribute("style")) (dragElement as any).style.pointerEvents = "none"; - for (let i = 0; i < set.length; i++) + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < set.length; i++) { if (set[i].hasAttribute("style")) { let s = set[i]; (s as any).style.pointerEvents = "none"; } + } dragDiv.appendChild(dragElement); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index e1e595925..943cdb4d1 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -156,12 +156,12 @@ export const nodes: { [index: string]: NodeSpec } = { title: dom.getAttribute("title"), alt: dom.getAttribute("alt"), width: Math.min(100, Number(dom.getAttribute("width"))), - } + }; } }], toDOM(node) { - const attrs = { style: `width: ${node.attrs.width}` } - return ["video", { ...node.attrs, ...attrs }] + const attrs = { style: `width: ${node.attrs.width}` }; + return ["video", { ...node.attrs, ...attrs }]; } }, @@ -285,7 +285,7 @@ export const marks: { [index: string]: MarkSpec } = { toDOM() { return ['span', { style: 'color: blue' - }] + }]; } }, @@ -536,4 +536,4 @@ schema.nodeFromJSON = (json: any) => { node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice); } return node; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 0a61b7e7d..f2559db74 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -256,7 +256,7 @@ export class TooltipTextMenu { starButton.onclick = () => { let state = this.view.state; this.insertStar(state, this.view.dispatch); - } + }; this.tooltip.appendChild(starButton); } diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index da374455e..eb1937683 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,8 +1,8 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; import { observable, action } from "mobx"; -import { observer } from "mobx-react" -import "./ContextMenu.scss" +import { observer } from "mobx-react"; +import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ceca940b6..e60f8c86c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> SelectionManager.SelectedDocuments().forEach(element => { const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); - if (rect.width !== 0 && (dX != 0 || dY != 0 || dW != 0 || dH != 0)) { + if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) { let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 0de880175..b4ad5f4d7 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -40,7 +40,7 @@ export class MainOverlayTextBox extends React.Component this.TextDoc = box.props.Document; let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); }; - this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content") + this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content"); } else { this.TextDoc = undefined; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index fd76cbbd3..7f979cd3b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -237,10 +237,11 @@ export class MainView extends React.Component { } onColorClick = (e: React.MouseEvent) => { - let target = (e.nativeEvent! as any).path[0]; - let parent = (e.nativeEvent! as any).path[1]; - if (target.localName === "input" || parent.localName === "span") + let target = (e.nativeEvent as any).path[0]; + let parent = (e.nativeEvent as any).path[1]; + if (target.localName === "input" || parent.localName === "span") { e.stopPropagation(); + } } diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index d2d41a4ba..1dacbb663 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -40,8 +40,8 @@ class PresentationViewList extends React.Component { //this doc is selected className += " presentationView-selected"; } - let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; } - let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; } + let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; }; + let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; }; return (
    { @action.bound removeDocument(doc: Doc): boolean { - let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView) + let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); docView && SelectionManager.DeselectDoc(docView); const props = this.props; //TODO This won't create the field if it doesn't already exist diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f2b3528b8..5beb89315 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -347,7 +347,7 @@ export class CollectionDockingView extends React.Component CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); @@ -432,8 +432,9 @@ export class DockedFrameRenderer extends React.Component { @observable private _document: Opt; get _stack(): any { let parent = (this.props as any).glContainer.parent.parent; - if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) + if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) { return parent.parent.contentItems[1]; + } return parent; } constructor(props: any) { diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index b62d3f7bb..b2d016934 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -40,7 +40,7 @@ export class CollectionPDFView extends React.Component { // console.log(this.props.Document[HeightSym]()); }, { fireImmediately: true } - ) + ); } public static LayoutString(fieldKey: string = "data") { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 14a7d19d0..faea8d44d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -87,8 +87,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { let columnDocs = DocListCast(schemaDoc.data); if (columnDocs) { let ddoc = columnDocs.find(doc => doc.title === columnName); - if (ddoc) + if (ddoc) { return ddoc; + } } } return this.props.Document; @@ -285,7 +286,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate( - - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth); + - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth) get documentKeysCheckList() { @@ -334,7 +335,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { columns={this.tableColumns} column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }} getTrProps={this.getTrProps} - /> + />; } @computed @@ -360,7 +361,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} setPreviewScript={this.setPreviewScript} previewScript={this.previewScript} - /> + />; } @action setPreviewScript = (script: string) => { @@ -409,7 +410,7 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth * this.contentScaling(); private PanelHeight = () => this.nativeHeight * this.contentScaling(); - private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()) + private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e1ac3505b..f5ad4ee95 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let childFocus = (doc: Doc) => { doc.libraryBrush = true; this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. - } + }; return (
    doc) { whenActiveChanged={this.props.whenActiveChanged} />
    ); - }) + }); } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b13694e9d..3e495b734 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -67,8 +67,8 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; } - @action onMouseLeave = () => { this._isOver = false; } + @action onMouseEnter = () => { this._isOver = true; }; + @action onMouseLeave = () => { this._isOver = false; }; onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); @@ -89,8 +89,8 @@ class TreeView extends React.Component { let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); - this._header!.current!.className = "treeViewItem-header" - if (inside && this._bulletType != BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + this._header!.current!.className = "treeViewItem-header"; + if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); @@ -192,7 +192,7 @@ class TreeView extends React.Component { if (inside) { let docList = Cast(this.props.document.data, listSpec(Doc)); if (docList !== undefined) { - addDoc = (doc: Doc) => { docList && docList.push(doc); return true; } + addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } let added = false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index c4dd534ed..be75c6c5c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -109,7 +109,7 @@ export class CollectionFreeFormLinksView extends React.Component [ , ...this.views - ]; + ] render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index dedcc3172..05dc6f534 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -321,7 +321,7 @@ export class MarqueeView extends React.Component summary.imageSummary = imageSummary; this.props.addDocument(imageSummary, false); } - }) + }); newCollection.proto!.summaryDoc = summary; selected = [newCollection]; newCollection.x = bounds.left + bounds.width; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f6b1c62ee..858959d81 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -48,7 +48,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom); + .scale(1 / this.contentScaling()).scale(1 / this.zoom) animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { this.props.bringToFront(this.props.Document); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7c058d91c..4992669df 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -168,8 +168,8 @@ export class DocumentView extends DocComponent(Docu public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { setTimeout(() => { let now = Date.now(); - let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1 - doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress + let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1; + doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress; doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; cb && cb(progress); if (now < stime + 200) { @@ -229,7 +229,7 @@ export class DocumentView extends DocComponent(Docu if (minimizedDoc) { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint( NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y)); - this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)) + this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)); } } @@ -372,8 +372,8 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } - deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); } - fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") }; + deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }; + fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -527,7 +527,7 @@ export class DocumentView extends DocComponent(Docu onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); - @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); } + @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 36d902c4f..df12f261b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -161,7 +161,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => { Cast(stackDoc.data, listSpec(Doc))!.push(doc); return true; - }) + }); } } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6b8b64c5f..f208ce2ce 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -188,8 +188,9 @@ export class ImageBox extends DocComponent(ImageD } @action onError = () => { let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; - if (timeout < 10) + if (timeout < 10) { setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + } } _curSuffix = "_m"; render() { diff --git a/src/client/views/pdf/PDFAnnotationLayer.tsx b/src/client/views/pdf/PDFAnnotationLayer.tsx index e92dcacbf..1f49e0d2f 100644 --- a/src/client/views/pdf/PDFAnnotationLayer.tsx +++ b/src/client/views/pdf/PDFAnnotationLayer.tsx @@ -19,6 +19,6 @@ export class PDFAnnotationLayer extends React.Component {
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 7817e8c26..2ed891131 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -162,6 +162,6 @@ export default class PDFMenu extends React.Component {
    - ) + ); } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 69372f43b..8c0aaea00 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -198,7 +198,7 @@ class Viewer extends React.Component { { @action getPageImage = async (page: number) => { let handleError = () => this.getRenderedPage(page); - if (this._isPage[page] != "image") { + if (this._isPage[page] !== "image") { this._isPage[page] = "image"; const address = this.props.url; let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); diff --git a/src/server/index.ts b/src/server/index.ts index 7ef542b01..2901f61ed 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -172,7 +172,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) { canvasContext: canvasAndContext.context, viewport: viewport, canvasFactory: factory - } + }; console.log("read " + pageNumber); page.render(renderContext).promise -- cgit v1.2.3-70-g09d2 From 3b880d7b15b7107049ae27601b9f759b17f7fde9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 18 Jun 2019 22:51:46 -0400 Subject: added initial keyboard shortcuts for adding and moving docs in TreeView. fixed image drag bug. --- src/client/documents/Documents.ts | 3 +- src/client/views/EditableView.tsx | 34 +++++--- .../views/collections/CollectionDockingView.tsx | 1 + .../views/collections/CollectionTreeView.tsx | 96 +++++++++++++++++----- src/client/views/nodes/ImageBox.tsx | 2 +- 5 files changed, 103 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 91d3707f6..fcd1010c6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -51,7 +51,6 @@ export interface DocumentOptions { panY?: number; page?: number; scale?: number; - baseLayout?: string; layout?: string; templates?: List; viewType?: number; @@ -136,7 +135,7 @@ export namespace Docs { } function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Doc { - return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout, baseLayout: layout }); + return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout }); } function SetInstanceOptions(doc: Doc, options: DocumentOptions, value: U) { const deleg = Doc.MakeDelegate(doc); diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index c946d68e1..a96fca464 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -18,13 +18,17 @@ export interface EditableProps { OnFillDown?(value: string): void; + OnTab?(): void; + /** * The contents to render when not editing */ contents: any; + fontStyle?: string; height?: number; display?: string; oneLine?: boolean; + editing?: boolean } /** @@ -34,40 +38,48 @@ export interface EditableProps { */ @observer export class EditableView extends React.Component { - @observable - editing: boolean = false; + @observable _editing: boolean = false; + + constructor(props: EditableProps) { + super(props); + this._editing = this.props.editing ? true : false; + } @action onKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { + if (e.key === "Tab") { + this.props.OnTab && this.props.OnTab(); + } else if (e.key === "Enter") { if (!e.ctrlKey) { if (this.props.SetValue(e.currentTarget.value)) { - this.editing = false; + this._editing = false; } } else if (this.props.OnFillDown) { this.props.OnFillDown(e.currentTarget.value); - this.editing = false; + this._editing = false; } } else if (e.key === "Escape") { - this.editing = false; + this._editing = false; } } @action onClick = (e: React.MouseEvent) => { - this.editing = true; + this._editing = true; e.stopPropagation(); } render() { - if (this.editing) { - return this.editing = false)} + if (this._editing) { + return this._editing = false)} style={{ display: this.props.display }} />; } else { return ( -
    - {this.props.contents} + {this.props.contents}
    ); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f2b3528b8..bd3020a78 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -467,6 +467,7 @@ export class DockedFrameRenderer extends React.Component { let docHeight = NumCast(this._document!.height); if (NumCast(this._document!.nativeWidth) || !docWidth || !this._panelWidth || !this._panelHeight) return 1; if (StrCast(this._document!.layout).indexOf("Collection") === -1 || + !BoolCast(this._document!.fitToContents, false) || NumCast(this._document!.viewType) !== CollectionViewType.Freeform) return 1; let scaling = Math.max(1, this._panelWidth / docWidth * docHeight > this._panelHeight ? this._panelHeight / docHeight : this._panelWidth / docWidth); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b13694e9d..23efe9f79 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -21,6 +21,7 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { Transform } from '../../util/Transform'; import { SelectionManager } from '../../util/SelectionManager'; +import { emptyFunction } from '../../../Utils'; export interface TreeViewProps { @@ -30,6 +31,7 @@ export interface TreeViewProps { dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; + indentDocument?: () => void; ScreenToLocalTransform: () => Transform; treeViewId: string; parentKey: string; @@ -114,6 +116,12 @@ class TreeView extends React.Component { } return true; } + @action + indent = () => { + this.props.addDocument(this.props.document); + this.delete(); + } + renderBullet(type: BulletType) { let onClicked = action(() => this._collapsed = !this._collapsed); @@ -124,25 +132,37 @@ class TreeView extends React.Component { } return
    {bullet ? : ""}
    ; } + static loadId = ""; + editableView = (key: string, style?: string) => + ( StrCast(this.props.document[key])} + OnFillDown={(value: string) => { + Doc.GetProto(this.props.document)[key] = value; + let doc = Docs.FreeformDocument([], { title: "untitled" }); + TreeView.loadId = doc[Id]; + this.props.addDocument(doc); + return true; + }} + OnTab={() => this.props.indentDocument && this.props.indentDocument()} + SetValue={(value: string) => { + Doc.GetProto(this.props.document)[key] = value; + return true; + }} + />); + /** * Renders the EditableView title element for placement into the tree. */ renderTitle() { let reference = React.createRef(); let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); - let editableView = (titleString: string) => - ( StrCast(this.props.document.title)} - SetValue={(value: string) => { - let target = this.props.document.proto ? this.props.document.proto : this.props.document; - target.title = value; - return true; - }} - />); + let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
    @@ -156,7 +176,7 @@ class TreeView extends React.Component { pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none" }} > - {editableView(StrCast(this.props.document.title))} + {this.editableView("title")} {/* {
    } */}
    {openRight} @@ -221,6 +241,7 @@ class TreeView extends React.Component { return true; } + @observable _chosenKey: string = "data" _bulletType: BulletType = BulletType.List; render() { let bulletType = BulletType.List; @@ -234,7 +255,21 @@ class TreeView extends React.Component { keys.splice(keys.indexOf("data"), 1); keys.splice(0, 0, "data"); } + let keyList: string[] = []; keys.map(key => { + let docList = Cast(this.props.document[key], listSpec(Doc)); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + keyList.push(key); + } + }); + let headerElements =
    {keyList.map(key => + this._chosenKey = key)} + style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}> + {key} + )} +
    + [this._chosenKey].map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before); @@ -243,9 +278,10 @@ class TreeView extends React.Component { if (!this._collapsed) { bulletType = BulletType.Collapsible; contentElement.push(
      - {key} + {headerElements}
      {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, + this.indent, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)}
    ); @@ -274,14 +310,36 @@ class TreeView extends React.Component { add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, + indent: () => void, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, active: () => boolean ) { - return docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)).map(child => - ); + let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); + return docList.map((child, i) => { + let indent = i == 0 ? undefined : () => { + if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { + let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1]; + let fieldKey = fieldKeysub.split("\"")[1]; + TreeView.AddDocToList(docList[i - 1], fieldKey, child); + remove(child); + } + } + return + }); } } @@ -321,7 +379,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active); + moveDoc, emptyFunction, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active); return (
    (ImageD let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; return ( -
    Date: Wed, 19 Jun 2019 09:11:35 -0400 Subject: fixed lint errors. --- src/client/views/EditableView.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index a96fca464..70d6c22bf 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -28,7 +28,7 @@ export interface EditableProps { height?: number; display?: string; oneLine?: boolean; - editing?: boolean + editing?: boolean; } /** diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6a2ab9d7d..f6c9ba86b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -154,7 +154,7 @@ class TreeView extends React.Component { Doc.GetProto(this.props.document)[key] = value; return true; }} - />); + />) /** * Renders the EditableView title element for placement into the tree. @@ -241,7 +241,7 @@ class TreeView extends React.Component { return true; } - @observable _chosenKey: string = "data" + @observable _chosenKey: string = "data"; _bulletType: BulletType = BulletType.List; render() { let bulletType = BulletType.List; @@ -268,7 +268,7 @@ class TreeView extends React.Component { style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}> {key} )} -
    +
    ; [this._chosenKey].map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); @@ -318,7 +318,7 @@ class TreeView extends React.Component { ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); return docList.map((child, i) => { - let indent = i == 0 ? undefined : () => { + let indent = i === 0 ? undefined : () => { if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1]; let fieldKey = fieldKeysub.split("\"")[1]; @@ -338,7 +338,7 @@ class TreeView extends React.Component { addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} parentKey={key} - active={active} /> + active={active} />; }); } } -- cgit v1.2.3-70-g09d2 From 0674331f3611d297028526c888c718a75b012e0a Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 09:36:21 -0400 Subject: fixed resizing stacking views. changed defaults for new docs in treeView --- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e60f8c86c..d8642d675 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -503,8 +503,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> proto.nativeWidth = (doc.width || 0) / doc.height * NumCast(proto.nativeHeight); } } else { - doc.width = zoomBasis * actualdW; - doc.height = zoomBasis * actualdH; + dW && (doc.width = zoomBasis * actualdW); + dH && (doc.height = zoomBasis * actualdH); proto.autoHeight = undefined; } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f6c9ba86b..5e690361c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -22,6 +22,8 @@ import React = require("react"); import { Transform } from '../../util/Transform'; import { SelectionManager } from '../../util/SelectionManager'; import { emptyFunction } from '../../../Utils'; +import { List } from '../../../new_fields/List'; +import { Templates } from '../Templates'; export interface TreeViewProps { @@ -144,8 +146,9 @@ class TreeView extends React.Component { GetValue={() => StrCast(this.props.document[key])} OnFillDown={(value: string) => { Doc.GetProto(this.props.document)[key] = value; - let doc = Docs.FreeformDocument([], { title: "untitled" }); + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); TreeView.loadId = doc[Id]; + doc.templates = new List([Templates.Title.Layout]); this.props.addDocument(doc); return true; }} -- cgit v1.2.3-70-g09d2 From f60398d5db9041e09c809c16a0b885936ac11a3d Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 10:21:37 -0400 Subject: fixed multi-column stacking --- .../views/collections/CollectionStackingView.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f5ad4ee95..f80ba0a4b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -96,8 +96,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { get children() { return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { let dref = React.createRef(); - let dxf = () => this.getDocTransform(d, dref.current!); - let rowSpan = Math.ceil((this.columnWidth / d[WidthSym]() * d[HeightSym]() + this.gridGap) / (this._gridSize + this.gridGap)); + let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]()); + let renderScale = this.columnWidth / NumCast(d.nativeWidth, this.columnWidth); + let aspect = NumCast(d.nativeWidth) / NumCast(d.nativeHeight); + let width = () => this.columnWidth; + let height = () => aspect ? width() / aspect : d[HeightSym]() + let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); let childFocus = (doc: Doc) => { doc.libraryBrush = true; this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. @@ -106,12 +110,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { key={d[Id]} ref={dref} style={{ - width: NumCast(d.nativeWidth, d[WidthSym]()), - height: NumCast(d.nativeHeight, d[HeightSym]()), + width: width(), + height: height(), transformOrigin: "top left", gridRowEnd: `span ${rowSpan}`, gridColumnEnd: `span 1`, - transform: `scale(${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())}, ${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())})` + transform: `scale(${renderScale})` }} > doc) { ScreenToLocalTransform={dxf} focus={childFocus} ContentScaling={returnOne} - PanelWidth={d[WidthSym]} - PanelHeight={d[HeightSym]} + PanelWidth={width} + PanelHeight={height} selectOnLoad={false} parentActive={this.props.active} addDocTab={this.props.addDocTab} -- cgit v1.2.3-70-g09d2 From 0bb20528c8167b3ba1c4c88d97586d50ae183b4c Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 10:37:36 -0400 Subject: added highlight for expanded tree view items --- src/client/views/collections/CollectionTreeView.scss | 5 +++++ src/client/views/collections/CollectionTreeView.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index f6df96d92..2d5092980 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -68,6 +68,11 @@ display: none; } +.treeViewItem-border { + display:inherit; + border-left: dashed 1px #00000042; +} + .treeViewItem-header:hover { .treeViewItem-openRight { display: inline-block; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 5e690361c..d43402e7d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -302,7 +302,9 @@ class TreeView extends React.Component { {this.renderBullet(bulletType)} {this.renderTitle()}
    - {contentElement} +
    + {contentElement} +
    ; } -- cgit v1.2.3-70-g09d2 From 37f327ab659e6fa1221f9f4ed7649402c5dedc00 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 11:24:32 -0400 Subject: aspect ratio, dragging, and full screen scrolling fixed --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 20 ++++++++++++++++---- src/client/views/pdf/PDFViewer.tsx | 15 ++++++++++----- src/client/views/pdf/Page.tsx | 10 +++------- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 7dd2b1dc5..d2de1cb1c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -97,7 +97,7 @@ export class PDFBox extends DocComponent(PdfDocumen render() { // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); - let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); + let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
    ; + private _dragging: boolean = false; constructor(props: Readonly<{}>) { super(props); @@ -35,19 +36,30 @@ export default class PDFMenu extends React.Component { } pointerDown = (e: React.PointerEvent) => { - document.removeEventListener("pointermove", this.StartDrag); - document.addEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointermove", this.pointerMove); + document.addEventListener("pointermove", this.pointerMove); document.removeEventListener("pointerup", this.pointerUp); document.addEventListener("pointerup", this.pointerUp); - console.log(this.StartDrag); + e.stopPropagation(); + e.preventDefault(); + } + pointerMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); + + if (this._dragging) { + return; + } + + this.StartDrag(e); + this._dragging = true; } pointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.StartDrag); + this._dragging = false; + document.removeEventListener("pointermove", this.pointerMove); document.removeEventListener("pointerup", this.pointerUp); e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 8c0aaea00..67278b1eb 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -90,7 +90,8 @@ class Viewer extends React.Component { @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 +115,16 @@ class Viewer extends React.Component { let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); this._isPage = Array(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); } } @@ -236,7 +241,7 @@ class Viewer extends React.Component { // 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 diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index a19b64eda..2697c9eee 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -51,7 +51,6 @@ export default class Page extends React.Component { private _marquee: React.RefObject; private _curly: React.RefObject; private _marqueeing: boolean = false; - private _dragging: boolean = false; private _reactionDisposer?: IReactionDisposer; constructor(props: IPageProps) { @@ -151,13 +150,9 @@ export default class Page extends React.Component { */ @action startDrag = (e: PointerEvent): void => { - // the first 5 lines is a hack to prevent text selection while dragging e.preventDefault(); e.stopPropagation(); - if (this._dragging) { - return; - } - this._dragging = true; + console.log("dragging"); let thisDoc = this.props.parent.Document; // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); @@ -179,7 +174,6 @@ export default class Page extends React.Component { endDrag = (e: PointerEvent): void => { // document.removeEventListener("pointermove", this.startDrag); // document.removeEventListener("pointerup", this.endDrag); - this._dragging = false; e.stopPropagation(); } @@ -195,6 +189,8 @@ export default class Page extends React.Component { // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { -- cgit v1.2.3-70-g09d2 From c056adeca11f35972b5f75c6b1cc31292d5765d4 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 11:47:20 -0400 Subject: push --- src/client/views/pdf/PDFViewer.tsx | 316 ++++++++++++++++++------------------- src/client/views/pdf/Page.tsx | 67 +------- 2 files changed, 154 insertions(+), 229 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 67278b1eb..f0e55705d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -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 { @@ -190,7 +191,7 @@ class Viewer extends React.Component { if (this._isPage[page] !== "none") { this._isPage[page] = "none"; this._visibleElements[page] = ( -
    ); } @@ -204,11 +205,11 @@ class Viewer extends React.Component { 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} @@ -285,27 +286,27 @@ class Viewer extends React.Component { 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([pinAnno]); - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - annotations.push(annoDoc); - this.props.parent.Document.annotations = new List(annotations); - } - else { - this.props.parent.Document.annotations = new List([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([pinAnno]); + // let annotations = DocListCast(this.props.parent.Document.annotations); + // if (annotations && annotations.length) { + // annotations.push(annoDoc); + // this.props.parent.Document.annotations = new List(annotations); + // } + // else { + // this.props.parent.Document.annotations = new List([annoDoc]); + // } + // } // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { @@ -339,26 +340,6 @@ class Viewer extends React.Component { 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 - }); } } @@ -367,8 +348,8 @@ class Viewer extends React.Component { let res = annotationDocs.map(a => { let type = NumCast(a.type); switch (type) { - case AnnotationTypes.Pin: - return ; + // case AnnotationTypes.Pin: + // return ; case AnnotationTypes.Region: return ; default: @@ -399,7 +380,7 @@ class Viewer extends React.Component { } export enum AnnotationTypes { - Region, Pin + Region } interface IAnnotationProps { @@ -411,132 +392,139 @@ interface IAnnotationProps { document: Doc; } -@observer -class PinAnnotation extends React.Component { - @observable private _backgroundColor: string = "green"; - @observable private _display: string = "initial"; - - private _mainCont: React.RefObject; - - 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(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 ( -
    -
    - 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} - /> -
    -
    - ); - } - return null; - } -} +// @observer +// class PinAnnotation extends React.Component { +// @observable private _backgroundColor: string = "green"; +// @observable private _display: string = "initial"; + +// private _mainCont: React.RefObject; + +// 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(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 ( +//
    +//
    +// 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} +// /> +//
    +//
    +// ); +// } +// return null; +// } +// } class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - onPointerDown = (e: React.MouseEvent) => { - let targetDoc = Cast(this.props.document.target, Doc, null); - if (targetDoc) { - DocumentManager.Instance.jumpToDocument(targetDoc); + 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) { + // console.log("right"); + // e.stopPropagation(); + // e.preventDefault(); + // } } render() { return ( -
    ); } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 2697c9eee..39e737c32 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -15,6 +15,7 @@ import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; import PDFMenu from "./PDFMenu"; +import { UndoManager } from "../../util/UndoManager"; interface IPageProps { @@ -152,7 +153,6 @@ export default class Page extends React.Component { startDrag = (e: PointerEvent): void => { e.preventDefault(); e.stopPropagation(); - console.log("dragging"); let thisDoc = this.props.parent.Document; // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); @@ -163,7 +163,7 @@ export default class Page extends React.Component { if (this._textLayer.current) { DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { handlers: { - dragComplete: action(emptyFunction), + dragComplete: emptyFunction, }, hideSource: false }); @@ -325,68 +325,6 @@ export default class Page extends React.Component { PDFMenu.Instance.StartDrag = this.startDrag; PDFMenu.Instance.Highlight = this.highlight; } - // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - // if (this._marqueeing) { - // this._marqueeing = false; - // if (this._marquee.current) { - // let copy = document.createElement("div"); - // // make a copy of the marquee - // copy.style.left = this._marquee.current.style.left; - // copy.style.top = this._marquee.current.style.top; - // copy.style.width = this._marquee.current.style.width; - // copy.style.height = this._marquee.current.style.height; - - // // apply the appropriate background, opacity, and transform - // let { background, opacity, transform } = this.getCurlyTransform(); - // copy.style.background = background; - // // if curly bracing, add a curly brace - // if (opacity === "1" && this._curly.current) { - // copy.style.opacity = opacity; - // let img = this._curly.current.cloneNode(); - // (img as any).style.opacity = opacity; - // (img as any).style.transform = transform; - // copy.appendChild(img); - // } - // else { - // copy.style.opacity = this._marquee.current.style.opacity; - // } - // copy.className = this._marquee.current.className; - // this.props.createAnnotation(copy, this.props.page); - // this._marquee.current.style.opacity = "0"; - // } - - // this._marqueeHeight = this._marqueeWidth = 0; - // } - // else { - // let sel = window.getSelection(); - // // if selecting over a range of things - // if (sel && sel.type === "Range") { - // let clientRects = sel.getRangeAt(0).getClientRects(); - // if (this._textLayer.current) { - // let boundingRect = this._textLayer.current.getBoundingClientRect(); - // for (let i = 0; i < clientRects.length; i++) { - // let rect = clientRects.item(i); - // if (rect) { - // let annoBox = document.createElement("div"); - // annoBox.className = "pdfViewer-annotationBox"; - // // transforms the positions from screen onto the pdf div - // annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - // annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - // annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - // annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - // this.props.createAnnotation(annoBox, this.props.page); - // } - // } - // } - // // clear selection - // if (sel.empty) { // Chrome - // sel.empty(); - // } else if (sel.removeAllRanges) { // Firefox - // sel.removeAllRanges(); - // } - // } - // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } @@ -399,7 +337,6 @@ export default class Page extends React.Component { for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) { - console.log(rect); let annoBox = document.createElement("div"); annoBox.className = "pdfViewer-annotationBox"; // transforms the positions from screen onto the pdf div -- cgit v1.2.3-70-g09d2 From 46d57bc21cda4703855b85a4603bd471975d845b Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:25:47 -0400 Subject: deleting annotations --- src/client/views/pdf/PDFMenu.tsx | 40 +++++++++++++++++---- src/client/views/pdf/PDFViewer.tsx | 74 ++++++++++++++++++++++++++++++++------ src/client/views/pdf/Page.tsx | 1 + 3 files changed, 97 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2462a5f94..5dd7b4bcd 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -19,9 +19,11 @@ export default class PDFMenu extends React.Component { StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; - @observable Highlighting: boolean = false; + Delete: () => void = emptyFunction; + + @observable public Highlighting: boolean = false; + @observable public Status: "pdf" | "annotation" | "" = ""; - private _timeout: NodeJS.Timeout | undefined; private _offsetY: number = 0; private _offsetX: number = 0; private _mainCont: React.RefObject; @@ -66,8 +68,8 @@ export default class PDFMenu extends React.Component { } @action - jumpTo = (x: number, y: number) => { - if (!this._pinned) { + jumpTo = (x: number, y: number, forceJump: boolean = false) => { + if (!this._pinned || forceJump) { this._transition = this._transitionDelay = ""; this._opacity = 1; this._left = x; @@ -159,11 +161,35 @@ export default class PDFMenu extends React.Component { } } + deleteClicked = (e: React.PointerEvent) => { + this.Delete(); + } + + handleContextMenu = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + } + render() { + let buttons = this.Status === "pdf" ? [ + , + , + + ] : [ + + ]; + return ( -
    - @@ -171,7 +197,7 @@ export default class PDFMenu extends React.Component { + */}
    ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index f0e55705d..ea4c0bca2 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"; @@ -138,6 +138,7 @@ class Viewer extends React.Component { 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(); @@ -147,6 +148,7 @@ class Viewer extends React.Component { 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); @@ -154,13 +156,12 @@ class Viewer extends React.Component { } }); - let annoDoc = new Doc(); - annoDoc.annotations = new List(annoDocs); + mainAnnoDoc.annotations = new List(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) => { @@ -508,6 +509,57 @@ interface IAnnotationProps { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; + private _reactionDisposer?: IReactionDisposer; + private _mainCont: React.RefObject; + + constructor(props: IAnnotationProps) { + super(props); + + this._mainCont = React.createRef(); + } + + 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 } + ); + } + + componentWillUnmount() { + this._reactionDisposer && this._reactionDisposer(); + } + + 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(newAnnotations); + } + + if (group) { + let groupAnnotations = DocListCast(group.annotations); + groupAnnotations.forEach(anno => anno.delete = true); + } + } + + + // 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); @@ -515,16 +567,16 @@ class RegionAnnotation extends React.Component { DocumentManager.Instance.jumpToDocument(targetDoc); } } - // if (e.button === 2) { - // console.log("right"); - // e.stopPropagation(); - // e.preventDefault(); - // } + if (e.button === 2) { + PDFMenu.Instance.Status = "annotation"; + PDFMenu.Instance.Delete = this.deleteAnnotation; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); + } } render() { return ( -
    ); } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 39e737c32..734dff7fc 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -191,6 +191,7 @@ export default class Page extends React.Component { else if (e.button === 0) { PDFMenu.Instance.StartDrag = this.startDrag; PDFMenu.Instance.Highlight = this.highlight; + PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { -- cgit v1.2.3-70-g09d2 From b960a876d6a31b3eaebb0ac6eca6f191a0d4c900 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:38:43 -0400 Subject: oop --- src/client/views/pdf/PDFMenu.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 5dd7b4bcd..39b15fb11 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -15,7 +15,8 @@ export default class PDFMenu extends React.Component { @observable private _opacity: number = 1; @observable private _transition: string = "opacity 0.5s"; @observable private _transitionDelay: string = ""; - @observable private _pinned: boolean = false; + + @observable public Pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; @@ -69,7 +70,7 @@ export default class PDFMenu extends React.Component { @action jumpTo = (x: number, y: number, forceJump: boolean = false) => { - if (!this._pinned || forceJump) { + if (!this.Pinned || forceJump) { this._transition = this._transitionDelay = ""; this._opacity = 1; this._left = x; @@ -79,7 +80,7 @@ export default class PDFMenu extends React.Component { @action fadeOut = (forceOut: boolean) => { - if (!this._pinned) { + if (!this.Pinned) { if (this._opacity === 0.2) { this._transition = "opacity 0.1s"; this._transitionDelay = ""; @@ -98,7 +99,7 @@ export default class PDFMenu extends React.Component { @action pointerLeave = (e: React.PointerEvent) => { - if (!this._pinned) { + if (!this.Pinned) { this._transition = "opacity 0.5s"; this._transitionDelay = "1s"; this._opacity = 0.2; @@ -115,8 +116,8 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { - this._pinned = !this._pinned; - if (!this._pinned) { + this.Pinned = !this.Pinned; + if (!this.Pinned) { this.Highlighting = false; } } @@ -152,7 +153,7 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { - if (!this._pinned) { + if (!this.Pinned) { this.Highlight(undefined, "#f4f442"); } else { @@ -178,8 +179,8 @@ export default class PDFMenu extends React.Component { , , ] : [ @@ -198,7 +199,7 @@ export default class PDFMenu extends React.Component { style={this._pinned ? { backgroundColor: "#121212" } : {}}> */} -
    +
    ); } -- cgit v1.2.3-70-g09d2 From b9849810231e540a5898a56012abd32c197b23b5 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:39:15 -0400 Subject: anna --- src/client/views/pdf/PDFViewer.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ea4c0bca2..f3aa2f5a0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -570,6 +570,7 @@ class RegionAnnotation extends React.Component { 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); } } -- cgit v1.2.3-70-g09d2 From 9ab47393a2ce3d174ad3238422c2c310764be9af Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:40:28 -0400 Subject: interaction improvements with delete button --- src/client/views/pdf/PDFViewer.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index f3aa2f5a0..7000352e7 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -548,6 +548,8 @@ class RegionAnnotation extends React.Component { let groupAnnotations = DocListCast(group.annotations); groupAnnotations.forEach(anno => anno.delete = true); } + + PDFMenu.Instance.fadeOut(true); } -- cgit v1.2.3-70-g09d2 From 35e73f369a2145d8a042e0011a43e71763d57998 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 19 Jun 2019 15:02:48 -0400 Subject: added better search to context menu --- src/client/views/ContextMenu.scss | 23 ++++++++++ src/client/views/ContextMenu.tsx | 83 +++++++++++++++++++++++++----------- src/client/views/ContextMenuItem.tsx | 7 +-- src/client/views/MainView.tsx | 2 +- 4 files changed, 84 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index e363c5158..a1a2b06f1 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -53,6 +53,29 @@ font-size: 20px; } +.contextMenu-group { + // width: 11vw; //10vw + height: 30px; //2vh + background: rgb(200, 200, 200); + display: flex; //comment out to allow search icon to be inline with search text + justify-content: left; + align-items: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-width: .11px; + border-style: none; + border-color: $intermediate-color; // rgb(187, 186, 186); + border-bottom-style: solid; + // padding: 10px 0px 10px 0px; + white-space: nowrap; + font-size: 20px; +} + .contextMenu-item:hover { transition: all 0.1s; background: $lighter-alt-accent; diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index eb1937683..1133f70a1 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; -import { observable, action } from "mobx"; +import { observable, action, computed } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; @@ -17,11 +17,12 @@ export class ContextMenu extends React.Component { @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault(), icon: "smile" }]; @observable private _pageX: number = 0; @observable private _pageY: number = 0; - @observable private _display: string = "none"; + @observable private _display: boolean = false; @observable private _searchString: string = ""; // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be @observable private _yRelativeToTop: boolean = true; + private _searchRef = React.createRef(); private ref: React.RefObject; @@ -36,7 +37,6 @@ export class ContextMenu extends React.Component { @action clearItems() { this._items = []; - this._display = "none"; } @action @@ -62,34 +62,70 @@ export class ContextMenu extends React.Component { this._searchString = ""; - this._display = "flex"; - } - - intersects = (x: number, y: number): boolean => { - if (this.ref.current && this._display !== "none") { - let menuSize = { width: this.ref.current.getBoundingClientRect().width, height: this.ref.current.getBoundingClientRect().height }; + this._display = true; - let upperLeft = { x: this._pageX, y: this._yRelativeToTop ? this._pageY : window.innerHeight - (this._pageY + menuSize.height) }; - let bottomRight = { x: this._pageX + menuSize.width, y: this._yRelativeToTop ? this._pageY + menuSize.height : window.innerHeight - this._pageY }; - - if (x >= upperLeft.x && x <= bottomRight.x) { - if (y >= upperLeft.y && y <= bottomRight.y) { - return true; - } - } + if (this._searchRef.current) { + this._searchRef.current.focus(); } - return false; } @action closeMenu = () => { this.clearItems(); + this._display = false; } - render() { - let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY, display: this._display } : - { left: this._pageX, bottom: this._pageY, display: this._display }; + @computed get filteredItems() { + const searchString = this._searchString.toLowerCase().split(" "); + const matches = (descriptions: string[]): boolean => { + return searchString.every(s => descriptions.some(desc => desc.includes(s))); + }; + const createGroupHeader = (contents: any) => { + return ( +
    +
    {contents}
    +
    + ); + }; + const createItem = (item: ContextMenuProps) => ; + const flattenItems = (items: ContextMenuProps[], groupFunc: (contents: any) => JSX.Element, getPath: () => string[]) => { + let eles: JSX.Element[] = []; + + for (const item of items) { + const description = item.description.toLowerCase(); + const path = [...getPath(), description]; + if ("subitems" in item) { + const children = flattenItems(item.subitems, contents => groupFunc(<>{item.description} -> {contents}), () => path); + if (children.length || matches(path)) { + eles.push(groupFunc(item.description)); + eles = eles.concat(children); + } + } else { + if (!matches(path)) { + continue; + } + eles.push(createItem(item)); + } + } + return eles; + }; + return flattenItems(this._items, createGroupHeader, () => []); + } + + @computed get menuItems() { + if (!this._searchString) { + return this._items.map(item => ); + } + return this.filteredItems; + } + + render() { + if (!this._display) { + return null; + } + let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY } : + { left: this._pageX, bottom: this._pageY }; return (
    @@ -97,10 +133,9 @@ export class ContextMenu extends React.Component { - + - {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1). - map(prop => )} + {this.menuItems}
    ); } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index dc0751049..88ebd95bc 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -21,9 +21,6 @@ export interface SubmenuProps { closeMenu?: () => void; } -export interface ContextMenuItemProps { - type: ContextMenuProps | SubmenuProps; -} export type ContextMenuProps = OriginalMenuProps | SubmenuProps; @observer @@ -67,7 +64,6 @@ export class ContextMenuItem extends React.Component { return; } this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout); - } render() { @@ -84,8 +80,7 @@ export class ContextMenuItem extends React.Component {
    ); - } - else { + } else if ("subitems" in this.props) { let submenu = !this.overItem ? (null) :
    {this._items.map(prop => )} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7f979cd3b..e3d4ff8b5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -118,7 +118,7 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { - ContextMenu.Instance.clearItems(); + ContextMenu.Instance.closeMenu(); } }), true); } -- cgit v1.2.3-70-g09d2 From 358437eeafe42e029ffe27702bde15a3fad54a3b Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 16:17:18 -0400 Subject: working version of embedded tree view docs. --- .../views/collections/CollectionStackingView.tsx | 2 + .../views/collections/CollectionTreeView.scss | 15 ++- .../views/collections/CollectionTreeView.tsx | 132 +++++++++++++++------ src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FormattedTextBox.tsx | 1 + 5 files changed, 109 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f80ba0a4b..368e94a8c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -10,6 +10,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; +import { auto } from "async"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -112,6 +113,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { style={{ width: width(), height: height(), + overflow: "auto", transformOrigin: "top left", gridRowEnd: `span ${rowSpan}`, gridColumnEnd: `span 1`, diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2d5092980..ec78fdb80 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -12,7 +12,7 @@ padding-right: 0px; background: $light-color-secondary; font-size: 13px; - overflow: scroll; + overflow: auto; ul { list-style: none; @@ -50,10 +50,12 @@ font-size: 24px; } - .collectionTreeView-keyHeader { - font-style: italic; - font-size: 8pt; - } +} +.collectionTreeView-keyHeader { + font-style: italic; + font-size: 8pt; + margin-left: 3px; + display:none; } .docContainer { @@ -74,6 +76,9 @@ } .treeViewItem-header:hover { + .collectionTreeView-keyHeader { + display:inherit; + } .treeViewItem-openRight { display: inline-block; height:13px; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d43402e7d..1eab541b3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,9 +1,9 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace } from "mobx"; +import { action, observable, trace, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { Document, listSpec } from '../../../new_fields/Schema'; import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; @@ -16,14 +16,18 @@ import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { Transform } from '../../util/Transform'; import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; import { List } from '../../../new_fields/List'; import { Templates } from '../Templates'; +import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { number } from 'prop-types'; +import ReactTable from 'react-table'; +import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { @@ -32,9 +36,12 @@ export interface TreeViewProps { moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; + panelWidth: () => number; + panelHeight: () => number; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; + outerXf: () => number[]; treeViewId: string; parentKey: string; active: () => boolean; @@ -58,11 +65,13 @@ library.add(faCaretRight); class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer && this.treedropDisposer(); if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } + this._mainEle = ele; } @observable _isOver: boolean = false; @@ -76,7 +85,7 @@ class TreeView extends React.Component { onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); - if (e.buttons === 1) { + if (e.buttons === 1 && SelectionManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -166,6 +175,28 @@ class TreeView extends React.Component { let reference = React.createRef(); let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + let keyList: string[] = []; + let keys = Array.from(Object.keys(this.props.document)); + if (this.props.document.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.props.document.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); + } + if (keys.indexOf("data") !== -1) { + keys.splice(keys.indexOf("data"), 1); + keys.splice(0, 0, "data"); + } + keys.map(key => { + let docList = Cast(this.props.document[key], listSpec(Doc)); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + keyList.push(key); + } + }); + let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => + { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} + style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> + {key} + ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
    @@ -182,6 +213,7 @@ class TreeView extends React.Component { {this.editableView("title")} {/* {
    } */}
    + {headerElements} {openRight} ; } @@ -244,34 +276,20 @@ class TreeView extends React.Component { return true; } + docTransform = () => { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); + let outerXf = this.props.outerXf(); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); + return finalXf; + } @observable _chosenKey: string = "data"; _bulletType: BulletType = BulletType.List; + + _dref = React.createRef(); render() { let bulletType = BulletType.List; let contentElement: (JSX.Element | null)[] = []; - let keys = Array.from(Object.keys(this.props.document)); - if (this.props.document.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.props.document.proto))); - while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); - } - if (keys.indexOf("data") !== -1) { - keys.splice(keys.indexOf("data"), 1); - keys.splice(0, 0, "data"); - } - let keyList: string[] = []; - keys.map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - keyList.push(key); - } - }); - let headerElements =
    {keyList.map(key => - this._chosenKey = key)} - style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} - )} -
    ; [this._chosenKey].map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); @@ -280,14 +298,31 @@ class TreeView extends React.Component { if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { if (!this._collapsed) { bulletType = BulletType.Collapsible; - contentElement.push(
      - {headerElements} -
      - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.indent, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)} -
      -
    ); + if (this.props.document.embed) { + contentElement.push( +
    +
    ); + } else + contentElement.push(
      +
      + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} +
      +
    ); } else { bulletType = BulletType.Collapsed; } @@ -315,11 +350,13 @@ class TreeView extends React.Component { add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, - indent: () => void, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - active: () => boolean + outerXf: () => number[], + active: () => boolean, + panelWidth: () => number, + panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); return docList.map((child, i) => { @@ -338,10 +375,13 @@ class TreeView extends React.Component { indentDocument={indent} deleteDoc={remove} addDocument={add} + panelWidth={panelWidth} + panelHeight={panelHeight} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} + outerXf={outerXf} parentKey={key} active={active} />; }); @@ -351,6 +391,7 @@ class TreeView extends React.Component { @observer export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { if (this.treedropDisposer) { this.treedropDisposer(); @@ -358,6 +399,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } + this._mainEle = ele; } @action @@ -373,6 +415,10 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } + outerXf = () => { + let outerXf = Utils.GetScreenTransform(this._mainEle!); + return [outerXf.translateX, outerXf.translateY]; + } onTreeDrop = (e: React.DragEvent) => { this.onDrop(e, {}); } @@ -384,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, emptyFunction, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active); + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); return (
    StrCast(this.props.Document.title)} + OnFillDown={(value: string) => { + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + TreeView.loadId = doc[Id]; + doc.templates = new List([Templates.Title.Layout]); + this.props.addDocument(doc); + }} SetValue={(value: string) => { let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; target.title = value; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4992669df..f7836d947 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,6 +72,7 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; + useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -538,8 +539,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
    Date: Wed, 19 Jun 2019 16:21:03 -0400 Subject: started adding selection to context menu --- src/client/views/ContextMenu.scss | 4 +++ src/client/views/ContextMenu.tsx | 58 +++++++++++++++++++++++------------- src/client/views/ContextMenuItem.tsx | 6 ++-- 3 files changed, 45 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index a1a2b06f1..254163b53 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -53,6 +53,10 @@ font-size: 20px; } +.contextMenu-itemSelected { + background: rgb(136, 136, 136) +} + .contextMenu-group { // width: 11vw; //10vw height: 30px; //2vh diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 1133f70a1..33de57cfa 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; +import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; import { observable, action, computed } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; @@ -21,6 +21,7 @@ export class ContextMenu extends React.Component { @observable private _searchString: string = ""; // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be @observable private _yRelativeToTop: boolean = true; + @observable selectedIndex = -1; private _searchRef = React.createRef(); @@ -75,49 +76,63 @@ export class ContextMenu extends React.Component { this._display = false; } - @computed get filteredItems() { + @computed get filteredItems(): (OriginalMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(" "); const matches = (descriptions: string[]): boolean => { - return searchString.every(s => descriptions.some(desc => desc.includes(s))); + return searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); }; - const createGroupHeader = (contents: any) => { - return ( -
    -
    {contents}
    -
    - ); - }; - const createItem = (item: ContextMenuProps) => ; - const flattenItems = (items: ContextMenuProps[], groupFunc: (contents: any) => JSX.Element, getPath: () => string[]) => { - let eles: JSX.Element[] = []; + const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: any) => string[]) => { + let eles: (OriginalMenuProps | string[])[] = []; + const leaves: OriginalMenuProps[] = []; for (const item of items) { - const description = item.description.toLowerCase(); - const path = [...getPath(), description]; + const description = item.description; + const path = groupFunc(description); if ("subitems" in item) { - const children = flattenItems(item.subitems, contents => groupFunc(<>{item.description} -> {contents}), () => path); + const children = flattenItems(item.subitems, name => [...groupFunc(description), name]); if (children.length || matches(path)) { - eles.push(groupFunc(item.description)); + eles.push(path); eles = eles.concat(children); } } else { if (!matches(path)) { continue; } - eles.push(createItem(item)); + leaves.push(item); } } + eles = [...leaves, ...eles]; + return eles; }; - return flattenItems(this._items, createGroupHeader, () => []); + return flattenItems(this._items, name => [name]); + } + + @computed get filteredViews() { + const createGroupHeader = (contents: any) => { + return ( +
    +
    {contents}
    +
    + ); + }; + const createItem = (item: ContextMenuProps, selected: boolean) => ; + let itemIndex = 0; + return this.filteredItems.map(value => { + if (Array.isArray(value)) { + return createGroupHeader(value.join(" -> ")); + } else { + return createItem(value, ++itemIndex === this.selectedIndex); + } + }); } @computed get menuItems() { if (!this._searchString) { return this._items.map(item => ); } - return this.filteredItems; + return this.filteredViews; } render() { @@ -143,5 +158,8 @@ export class ContextMenu extends React.Component { @action onChange = (e: React.ChangeEvent) => { this._searchString = e.target.value; + if (this._searchString && this.selectedIndex === -1) { + this.selectedIndex = 0; + } } } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 88ebd95bc..ebcac7428 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -24,7 +24,7 @@ export interface SubmenuProps { export type ContextMenuProps = OriginalMenuProps | SubmenuProps; @observer -export class ContextMenuItem extends React.Component { +export class ContextMenuItem extends React.Component { @observable private _items: Array = []; @observable private overItem = false; @@ -69,7 +69,7 @@ export class ContextMenuItem extends React.Component { render() { if ("event" in this.props) { return ( -
    +
    {this.props.icon ? ( @@ -86,7 +86,7 @@ export class ContextMenuItem extends React.Component { {this._items.map(prop => )}
    ; return ( -
    +
    {this.props.icon ? ( -- cgit v1.2.3-70-g09d2 From 05e50f27a15e8a02ffb27606c51026d1b85bc677 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 19 Jun 2019 17:36:52 -0400 Subject: Added basic keyboard controls to context menu --- src/client/views/ContextMenu.tsx | 40 ++++++++++++++++++++++++++++---- src/client/views/ContextMenuItem.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 41 +++++++++++++++++---------------- src/client/views/nodes/IconBox.tsx | 6 ++--- 4 files changed, 61 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 33de57cfa..59a0de2a0 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -14,7 +14,7 @@ library.add(faCircle); export class ContextMenu extends React.Component { static Instance: ContextMenu; - @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault(), icon: "smile" }]; + @observable private _items: Array = []; @observable private _pageX: number = 0; @observable private _pageY: number = 0; @observable private _display: boolean = false; @@ -109,6 +109,10 @@ export class ContextMenu extends React.Component { return flattenItems(this._items, name => [name]); } + @computed get flatItems(): OriginalMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + } + @computed get filteredViews() { const createGroupHeader = (contents: any) => { return ( @@ -123,7 +127,7 @@ export class ContextMenu extends React.Component { if (Array.isArray(value)) { return createGroupHeader(value.join(" -> ")); } else { - return createItem(value, ++itemIndex === this.selectedIndex); + return createItem(value, itemIndex++ === this.selectedIndex); } }); } @@ -148,18 +152,44 @@ export class ContextMenu extends React.Component { - + {this.menuItems}
    ); } + @action + onKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowDown") { + if (this.selectedIndex < this.flatItems.length - 1) { + this.selectedIndex++; + } + e.preventDefault(); + } else if (e.key === "ArrowUp") { + if (this.selectedIndex > 0) { + this.selectedIndex--; + } + e.preventDefault(); + } else if (e.key === "Enter") { + const item = this.flatItems[this.selectedIndex]; + item.event(); + this.closeMenu(); + } + } + @action onChange = (e: React.ChangeEvent) => { this._searchString = e.target.value; - if (this._searchString && this.selectedIndex === -1) { - this.selectedIndex = 0; + if (!this._searchString) { + this.selectedIndex = -1; + } + else { + if (this.selectedIndex === -1) { + this.selectedIndex = 0; + } else { + this.selectedIndex = Math.min(this.flatItems.length - 1, this.selectedIndex); + } } } } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index ebcac7428..9bbb97d7e 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -9,7 +9,7 @@ library.add(faAngleRight); export interface OriginalMenuProps { description: string; - event: (e: React.MouseEvent) => void; + event: () => void; icon?: IconProp; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -37,7 +37,7 @@ export class ContextMenuItem extends React.Component) => { if ("event" in this.props) { - this.props.event(e); + this.props.event(); this.props.closeMenu && this.props.closeMenu(); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..2062fe1b5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faShare, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import * as fa from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -34,23 +34,24 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? -library.add(faTrash); -library.add(faShare); -library.add(faExpandArrowsAlt); -library.add(faCompressArrowsAlt); -library.add(faLayerGroup); -library.add(faAlignCenter); -library.add(faCaretSquareRight); -library.add(faSquare); -library.add(faConciergeBell); -library.add(faFolder); -library.add(faMapPin); -library.add(faLink); -library.add(faFingerprint); -library.add(faCrosshairs); -library.add(faDesktop); -library.add(faUnlock); -library.add(faLock); +library.add(fa.faTrash); +library.add(fa.faShare); +library.add(fa.faExpandArrowsAlt); +library.add(fa.faCompressArrowsAlt); +library.add(fa.faLayerGroup); +library.add(fa.faExternalLinkAlt); +library.add(fa.faAlignCenter); +library.add(fa.faCaretSquareRight); +library.add(fa.faSquare); +library.add(fa.faConciergeBell); +library.add(fa.faFolder); +library.add(fa.faMapPin); +library.add(fa.faLink); +library.add(fa.faFingerprint); +library.add(fa.faCrosshairs); +library.add(fa.faDesktop); +library.add(fa.faUnlock); +library.add(fa.faLock); const linkSchema = createSchema({ title: "string", @@ -447,7 +448,7 @@ export class DocumentView extends DocComponent(Docu this.templates = this.templates; } - freezeNativeDimensions = (e: React.MouseEvent): void => { + freezeNativeDimensions = (): void => { let proto = Doc.GetProto(this.props.Document); if (proto.ignoreAspect === undefined && !proto.nativeWidth) { proto.nativeWidth = this.props.PanelWidth(); @@ -476,7 +477,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); - cm.addItem({ description: "Open...", subitems: subitems }); + cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: () => this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 00021bc78..d6ab2a34a 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -37,14 +37,14 @@ export class IconBox extends React.Component { return ; } - setLabelField = (e: React.MouseEvent): void => { + setLabelField = (): void => { this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel); } - setUseOwnTitleField = (e: React.MouseEvent): void => { + setUseOwnTitleField = (): void => { this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle); } - specificContextMenu = (e: React.MouseEvent): void => { + specificContextMenu = (): void => { ContextMenu.Instance.addItem({ description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon", event: this.setLabelField -- cgit v1.2.3-70-g09d2 From 96c26c57527d443784bde9752551bfa10b3ce4d2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Jun 2019 18:34:45 -0400 Subject: removed marquee summarizing icon --- .../collections/collectionFreeForm/MarqueeView.tsx | 43 ++++++++-------------- 1 file changed, 15 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 05dc6f534..3f7efcb66 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -302,35 +302,22 @@ export class MarqueeView extends React.Component this.props.addLiveTextDocument(container); // }); } else if (e.key === "S") { - await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => { - selected.map(d => { - this.props.removeDocument(d); - d.x = NumCast(d.x) - bounds.left - bounds.width / 2; - d.y = NumCast(d.y) - bounds.top - bounds.height / 2; - d.page = -1; - return d; - }); - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); - SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { - if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); - let imageSummary = Docs.ImageDocument(url, { - x: bounds.left, y: bounds.top + 100 / zoomBasis, - width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" - }); - summary.imageSummary = imageSummary; - this.props.addDocument(imageSummary, false); - } - }); - newCollection.proto!.summaryDoc = summary; - selected = [newCollection]; - newCollection.x = bounds.left + bounds.width; - //this.props.addDocument(newCollection, false); - summary.proto!.summarizedDocs = new List(selected); - summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" - - this.props.addLiveTextDocument(summary); + selected.map(d => { + this.props.removeDocument(d); + d.x = NumCast(d.x) - bounds.left - bounds.width / 2; + d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.page = -1; + return d; }); + let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); + newCollection.proto!.summaryDoc = summary; + selected = [newCollection]; + newCollection.x = bounds.left + bounds.width; + //this.props.addDocument(newCollection, false); + summary.proto!.summarizedDocs = new List(selected); + summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" + + this.props.addLiveTextDocument(summary); } else { this.props.addDocument(newCollection, false); -- cgit v1.2.3-70-g09d2 From 118ecb14ce519bcbade12b3d52e11b22fcc371b3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Jun 2019 22:40:54 -0400 Subject: cleaned up and enhanced tree view --- src/client/documents/Documents.ts | 1 - .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 5 +- .../views/collections/CollectionTreeView.tsx | 347 +++++++++------------ src/client/views/nodes/DocumentView.tsx | 5 +- 5 files changed, 158 insertions(+), 202 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fcd1010c6..de6c5bc6a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -18,7 +18,6 @@ import { action } from "mobx"; import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel"; import { AggregateFunction } from "../northstar/model/idea/idea"; -import { Template } from "../views/Templates"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index faea8d44d..9cc8961e3 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -385,7 +385,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; width: () => number; height: () => number; - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index ec78fdb80..a85604e58 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -4,10 +4,10 @@ border-width: $COLLECTION_BORDER_WIDTH; border-color: transparent; border-style: solid; - border-radius: $border-radius; + border-radius: inherit; box-sizing: border-box; height: 100%; - padding: 20px; + padding-top: 20px; padding-left: 10px; padding-right: 0px; background: $light-color-secondary; @@ -56,6 +56,7 @@ font-size: 8pt; margin-left: 3px; display:none; + background: lightgray; } .docContainer { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1eab541b3..856430036 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,38 +1,35 @@ -import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace, computed } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; +import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { SelectionManager } from '../../util/SelectionManager'; +import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; +import { Templates } from '../Templates'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionSchemaPreview } from './CollectionSchemaView'; +import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { Transform } from '../../util/Transform'; -import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; -import { List } from '../../../new_fields/List'; -import { Templates } from '../Templates'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; -import { number } from 'prop-types'; -import ReactTable from 'react-table'; -import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { document: Doc; - deleteDoc: (doc: Doc) => void; + deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; @@ -41,22 +38,18 @@ export interface TreeViewProps { addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; - outerXf: () => number[]; + outerXf: () => { translateX: number, translateY: number }; treeViewId: string; parentKey: string; active: () => boolean; } -export enum BulletType { - Collapsed, - Collapsible, - List -} - library.add(faTrashAlt); library.add(faAngleRight); library.add(faCaretDown); library.add(faCaretRight); +library.add(faCaretSquareDown); +library.add(faCaretSquareRight); @observer /** @@ -64,25 +57,22 @@ library.add(faCaretRight); */ class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); - private treedropDisposer?: DragManager.DragDropDisposer; - private _mainEle?: HTMLDivElement; + private _treedropDisposer?: DragManager.DragDropDisposer; + private _dref = React.createRef(); + @observable _chosenKey: string = "data"; + @observable _collapsed: boolean = true; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - this.treedropDisposer && this.treedropDisposer(); + this._treedropDisposer && this._treedropDisposer(); if (ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); + this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } - this._mainEle = ele; } - @observable _isOver: boolean = false; - @observable _collapsed: boolean = true; - @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; }; - @action onMouseLeave = () => { this._isOver = false; }; - + onPointerDown = (e: React.PointerEvent) => e.stopPropagation() onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -101,80 +91,60 @@ class TreeView extends React.Component { let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); this._header!.current!.className = "treeViewItem-header"; - if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + if (inside) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); } - onPointerDown = (e: React.PointerEvent) => { - e.stopPropagation(); - } @action - remove = (document: Document, key: string) => { + remove = (document: Document, key: string): boolean => { let children = Cast(this.props.document[key], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } @action - move: DragManager.MoveFunction = (document: Doc, target: Doc, addDoc) => { - if (this.props.document !== target) { - //TODO This should check if it was removed - this.props.deleteDoc(document); - return addDoc(document); - } - return true; + move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { + return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => { - this.props.addDocument(this.props.document); - this.delete(); - } - + indent = () => this.props.addDocument(this.props.document) && this.delete(); - renderBullet(type: BulletType) { - let onClicked = action(() => this._collapsed = !this._collapsed); - let bullet: IconProp | undefined = undefined; - switch (type) { - case BulletType.Collapsed: bullet = "caret-right"; break; - case BulletType.Collapsible: bullet = "caret-down"; break; - } - return
    {bullet ? : ""}
    ; + renderBullet() { + let docList = Cast(this.props.document["data"], listSpec(Doc)); + let doc = Cast(this.props.document["data"], Doc); + let isDoc = doc instanceof Doc || docList; + return
    this._collapsed = !this._collapsed)}> + {} +
    ; } - static loadId = ""; - editableView = (key: string, style?: string) => - ( StrCast(this.props.document[key])} - OnFillDown={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); - TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - return true; - }} - OnTab={() => this.props.indentDocument && this.props.indentDocument()} - SetValue={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - return true; - }} - />) - /** - * Renders the EditableView title element for placement into the tree. - */ - renderTitle() { - let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + static loadId = ""; + editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} + SetValue={(value: string) => (Doc.GetProto(this.props.document)[key] = value) ? true : true} + OnFillDown={(value: string) => { + Doc.GetProto(this.props.document)[key] = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + TreeView.loadId = doc[Id]; + return this.props.addDocument(doc); + }} + OnTab={() => this.props.indentDocument && this.props.indentDocument()} + />) + get keyList() { let keyList: string[] = []; let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { @@ -192,19 +162,26 @@ class TreeView extends React.Component { keyList.push(key); } }); - let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => - { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} - style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} + return keyList; + } + /** + * Renders the EditableView title element for placement into the tree. + */ + renderTitle() { + let reference = React.createRef(); + let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + + let headerElements = ( + this.props.document.embed = !BoolCast(this.props.document.embed))} > + {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
    - {/* */}
    ); return <> -
    { let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); if (de.data instanceof DragManager.DocumentDragData) { let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before); if (inside) { @@ -250,18 +227,13 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } - let added = false; - if (de.data.dropAction || de.data.userDropAction) { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false); - } else if (de.data.moveDocument) { - let movedDocs = de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments; - added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, this.props.document, addDoc) || added, false); - } else { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); - } e.stopPropagation(); - return added; + let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); + return (de.data.dropAction || de.data.userDropAction) ? + de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false) + : (de.data.moveDocument) ? + movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.props.document, addDoc) || added, false) + : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); } return false; } @@ -279,62 +251,47 @@ class TreeView extends React.Component { docTransform = () => { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); let outerXf = this.props.outerXf(); - let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); return finalXf; } - @observable _chosenKey: string = "data"; - _bulletType: BulletType = BulletType.List; - _dref = React.createRef(); render() { - let bulletType = BulletType.List; - let contentElement: (JSX.Element | null)[] = []; - [this._chosenKey].map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let remDoc = (doc: Doc) => this.remove(doc, key); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - if (!this._collapsed) { - bulletType = BulletType.Collapsible; - if (this.props.document.embed) { - contentElement.push( -
    -
    ); - } else - contentElement.push(
      -
      - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} -
      -
    ); - } else { - bulletType = BulletType.Collapsed; - } + let contentElement: (JSX.Element | null) = null; + let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc)); + let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let doc = Cast(this.props.document[this._chosenKey], Doc); + let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; + if (!this._collapsed) { + if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) { + contentElement =
      + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} +
    ; + } else { + contentElement =
    + + +
    } - }); - this._bulletType = bulletType; - return
    + } + return
  • - {this.renderBullet(bulletType)} + {this.renderBullet()} {this.renderTitle()}
    @@ -348,17 +305,17 @@ class TreeView extends React.Component { treeViewId: string, key: string, add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, - remove: ((doc: Doc) => void), + remove: ((doc: Doc) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - outerXf: () => number[], + outerXf: () => { translateX: number, translateY: number }, active: () => boolean, panelWidth: () => number, - panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); + let rowWidth = () => panelWidth() - 20; return docList.map((child, i) => { let indent = i === 0 ? undefined : () => { if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { @@ -368,15 +325,22 @@ class TreeView extends React.Component { remove(child); } } + let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { + return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); + } + let rowHeight = () => { + let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); + return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); + } return { export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - if (this.treedropDisposer) { - this.treedropDisposer(); - } - if (ele) { + this.treedropDisposer && this.treedropDisposer(); + if (this._mainEle = ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } - this._mainEle = ele; + } + + componentWillUnmount() { + this.treedropDisposer && this.treedropDisposer(); } @action - remove = (document: Document) => { + remove = (document: Document): boolean => { let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout @@ -415,26 +385,16 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } - outerXf = () => { - let outerXf = Utils.GetScreenTransform(this._mainEle!); - return [outerXf.translateX, outerXf.translateY]; - } - onTreeDrop = (e: React.DragEvent) => { - this.onDrop(e, {}); - } + outerXf = () => Utils.GetScreenTransform(this._mainEle!); + onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; - if (!this.childDocs) { - return (null); - } let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); - let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); - return ( + return !this.childDocs ? (null) : (
    this.props.isSelected() && e.stopPropagation()} onDrop={this.onTreeDrop} @@ -445,22 +405,19 @@ export class CollectionTreeView extends CollectionSubView(Document) { display={"inline"} height={72} GetValue={() => StrCast(this.props.Document.title)} + SetValue={(value: string) => (Doc.GetProto(this.props.Document).title = value) ? true : true} OnFillDown={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + Doc.GetProto(this.props.Document).title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - }} - SetValue={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - return true; + TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} />
      - {childElements} + { + TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth) + }
    ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..4992669df 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,7 +72,6 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; - useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -539,8 +538,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
    Date: Wed, 19 Jun 2019 23:43:47 -0400 Subject: slight tweaks. --- src/client/views/EditableView.tsx | 5 +++- .../views/collections/CollectionTreeView.tsx | 35 +++++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 70d6c22bf..0f6281b5c 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -29,6 +29,7 @@ export interface EditableProps { display?: string; oneLine?: boolean; editing?: boolean; + onClick?: (e: React.MouseEvent) => boolean; } /** @@ -65,7 +66,9 @@ export class EditableView extends React.Component { @action onClick = (e: React.MouseEvent) => { - this._editing = true; + if (!this.props.onClick || !this.props.onClick(e)) { + this._editing = true; + } e.stopPropagation(); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 856430036..c51c16883 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; @@ -125,12 +125,20 @@ class TreeView extends React.Component {
    ; } + titleClicked = (e: React.MouseEvent) => { + if (this._collapsed) return false; + else { + this.props.document.embed = !BoolCast(this.props.document.embed); + return true; + } + } static loadId = ""; editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} @@ -144,24 +152,24 @@ class TreeView extends React.Component { OnTab={() => this.props.indentDocument && this.props.indentDocument()} />) - get keyList() { - let keyList: string[] = []; + @computed get keyList() { let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { keys.push(...Array.from(Object.keys(this.props.document.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } - if (keys.indexOf("data") !== -1) { - keys.splice(keys.indexOf("data"), 1); - keys.splice(0, 0, "data"); - } + let keyList: string[] = []; keys.map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + if (doc instanceof Doc || docList) { keyList.push(key); } }); + if (keyList.indexOf("data") !== -1) { + keyList.splice(keyList.indexOf("data"), 1); + } + keyList.splice(0, 0, "data"); return keyList; } /** @@ -172,7 +180,12 @@ class TreeView extends React.Component { let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); let headerElements = ( - this.props.document.embed = !BoolCast(this.props.document.embed))} > + { + let ind = this.keyList.indexOf(this._chosenKey); + ind = (ind + 1) % this.keyList.length; + this._chosenKey = this.keyList[ind]; + })} > {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; @@ -209,7 +222,7 @@ class TreeView extends React.Component { } else { ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } - ContextMenu.Instance.displayMenu(e.pageX - 156, e.pageY - 15); + ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); } } @@ -264,7 +277,7 @@ class TreeView extends React.Component { let doc = Cast(this.props.document[this._chosenKey], Doc); let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; if (!this._collapsed) { - if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) { + if (!this.props.document.embed) { contentElement =
      {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} -- cgit v1.2.3-70-g09d2 From 01aee875e626c695fe208addaaa6f58aad387dd6 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 10:02:08 -0400 Subject: Mostly keep context menu on screen --- src/client/views/ContextMenu.tsx | 74 +++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 59a0de2a0..fd2f970da 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -6,6 +6,7 @@ import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons'; +import Measure from "react-measure"; library.add(faSearch); library.add(faCircle); @@ -23,15 +24,12 @@ export class ContextMenu extends React.Component { @observable private _yRelativeToTop: boolean = true; @observable selectedIndex = -1; - private _searchRef = React.createRef(); - - private ref: React.RefObject; + @observable private _width: number = 0; + @observable private _height: number = 0; constructor(props: Readonly<{}>) { super(props); - this.ref = React.createRef(); - ContextMenu.Instance = this; } @@ -51,23 +49,42 @@ export class ContextMenu extends React.Component { return this._items; } + static readonly buffer = 20; + get pageX() { + const x = this._pageX; + if (x < 0) { + return 0; + } + const width = this._width; + if (x + width > window.innerWidth - ContextMenu.buffer) { + return window.innerWidth - ContextMenu.buffer - width; + } + return x; + } + + get pageY() { + const y = this._pageY; + if (y < 0) { + return 0; + } + const height = this._height; + if (y + height > window.innerHeight - ContextMenu.buffer) { + return window.innerHeight - ContextMenu.buffer - height; + } + return y; + } + @action displayMenu(x: number, y: number) { //maxX and maxY will change if the UI/font size changes, but will work for any amount //of items added to the menu - let maxX = window.innerWidth - 150; - let maxY = window.innerHeight - ((this._items.length + 1/*for search box*/) * 34 + 30); - this._pageX = x > maxX ? maxX : x; - this._pageY = y > maxY ? maxY : y; + this._pageX = x; + this._pageY = y; this._searchString = ""; this._display = true; - - if (this._searchRef.current) { - this._searchRef.current.focus(); - } } @action @@ -143,19 +160,28 @@ export class ContextMenu extends React.Component { if (!this._display) { return null; } - let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY } : - { left: this._pageX, bottom: this._pageY }; + let style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } : + { left: this.pageX, bottom: this.pageY }; + + console.log(this._pageX); + console.log(this.pageX); + console.log(); return ( -
      - - - - - - - {this.menuItems} -
      + { this._width = r.offset.width; this._height = r.offset.height; })}> + {({ measureRef }) => ( +
      + + + + + + + {this.menuItems} +
      + ) + } +
      ); } -- cgit v1.2.3-70-g09d2 From a5478b2d4cc3b66c6b58471cbb05c623d0109724 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 10:04:51 -0400 Subject: "Fixed" search --- src/server/Search.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/server/Search.ts b/src/server/Search.ts index fd6ef36a6..d776480c6 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -7,7 +7,6 @@ export class Search { private url = 'http://localhost:8983/solr/'; public async updateDocument(document: any) { - return; try { const res = await rp.post(this.url + "dash/update", { headers: { 'content-type': 'application/json' }, -- cgit v1.2.3-70-g09d2 From e9d62f4ca0dbeb57e46239047041a8a04da7b504 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 20 Jun 2019 11:26:16 -0400 Subject: changed color picker. fixed delting selected docs. fixed scaling items in nested panels. --- src/client/util/SelectionManager.ts | 2 +- src/client/views/DocumentDecorations.tsx | 6 ++---- src/client/views/InkingControl.tsx | 11 +++++++++-- src/client/views/MainView.tsx | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 09bccb1a0..7dbb81e76 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -66,7 +66,7 @@ export namespace SelectionManager { export function GetIsDragging() { return manager.IsDragging; } export function SelectedDocuments(): Array { - return manager.SelectedDocuments; + return manager.SelectedDocuments.slice(); } export function ViewsSortedHorizontally(): DocumentView[] { let sorted = SelectionManager.SelectedDocuments().slice().sort((doc1, doc2) => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d8642d675..2c0e18bbb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -464,16 +464,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> runInAction(() => FormattedTextBox.InputBoxOverlay = undefined); SelectionManager.SelectedDocuments().forEach(element => { - const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); - - if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) { + if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; let zoomBasis = NumCast(doc.zoomBasis, 1); let width = (doc.width || 0) / zoomBasis; let height = (doc.height || (nheight / nwidth * width)) / zoomBasis; - let scale = width / rect.width; + let scale = element.props.ScreenToLocalTransform().Scale; let actualdW = Math.max(width + (dW * scale), 20); let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index b98132c23..0837e07a9 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -28,11 +28,18 @@ export class InkingControl extends React.Component { switchTool = (tool: InkTool): void => { this._selectedTool = tool; } + decimalToHexString(number: number) { + if (number < 0) { + number = 0xFFFFFFFF + number + 1; + } + + return number.toString(16).toUpperCase(); + } @action switchColor = (color: ColorResult): void => { - this._selectedColor = color.hex; - SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = color.hex); + this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); + SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); } @action diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e3d4ff8b5..51630c29b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker } from 'react-color'; +import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; import Measure from 'react-measure'; @@ -272,8 +272,8 @@ export class MainView extends React.Component {
    • {btns.map(btn => -- cgit v1.2.3-70-g09d2