From 464096297d34bb824ed665aa50dfb58f268c1d12 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Tue, 4 Jun 2019 20:01:59 -0400 Subject: BUttons in Presentation CSSed and grouping up insertion done, removal almost done --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7750b9173..984cac9b9 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -23,7 +23,7 @@ import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; import { DocComponent } from "../DocComponent"; -import { PresentationView } from "../PresentationView"; +import { PresentationView } from "../presentationview/PresentationView"; import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; -- cgit v1.2.3-70-g09d2 From a54e3b99c2c6d4c39cbdd935978b5e78015956ab Mon Sep 17 00:00:00 2001 From: madelinegr Date: Wed, 5 Jun 2019 17:29:43 -0400 Subject: Enums for buttons --- src/client/views/nodes/DocumentView.tsx | 4 +- .../views/presentationview/PresentationElement.tsx | 45 +++++++++++++++++----- .../views/presentationview/PresentationView.tsx | 5 +-- 3 files changed, 40 insertions(+), 14 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 984cac9b9..449fa76a7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -83,6 +83,7 @@ const schema = createSchema({ nativeWidth: "number", nativeHeight: "number", backgroundColor: "string", + opacity: "number" }); export const positionSchema = createSchema({ @@ -464,7 +465,8 @@ export class DocumentView extends DocComponent(Docu background: this.Document.backgroundColor || "", width: nativeWidth, height: nativeHeight, - transform: `scale(${scaling}, ${scaling})` + transform: `scale(${scaling}, ${scaling})`, + opacity: NumCast(this.props.Document.opacity, 1) }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index 08a663150..48e740c4b 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -19,6 +19,16 @@ interface PresentationElementProps { } +enum buttonIndex { + Show = 0, + Navigate = 1, + HideTillPressed = 2, + FadeAfter = 3, + HideAfter = 4, + Group = 5, + +} + @observer export default class PresentationElement extends React.Component { @@ -84,10 +94,10 @@ export default class PresentationElement extends React.Component { - if (this.selectedButtons[5]) { - this.selectedButtons[5] = false; + if (this.selectedButtons[buttonIndex.Group]) { + this.selectedButtons[buttonIndex.Group] = false; } else { - this.selectedButtons[5] = true; + this.selectedButtons[buttonIndex.Group] = true; } } @@ -95,6 +105,21 @@ export default class PresentationElement extends React.Component console.log("Index: ", index, " size: ", doc.length)); } + @action + onHideDocumentUntilPressClick = (e: React.MouseEvent) => { + e.stopPropagation(); + if (this.selectedButtons[buttonIndex.HideTillPressed]) { + this.selectedButtons[buttonIndex.HideTillPressed] = false; + + } else { + this.selectedButtons[buttonIndex.HideTillPressed] = true; + } + } + + hideDocumentIfNotPressed = () => { + this.props.allListElements.forEach((doc: Doc) => doc.opacity = 0); + } + render() { @@ -125,15 +150,15 @@ export default class PresentationElement extends React.Component

- - - - - - + + + + + diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx index 9baf75ad3..b41149099 100644 --- a/src/client/views/presentationview/PresentationView.tsx +++ b/src/client/views/presentationview/PresentationView.tsx @@ -55,7 +55,7 @@ class PresentationViewList extends React.Component { console.log("Starting len: ", this.props.groupedMembers.length); docList.forEach((doc: Doc, index: number) => { if (this.props.groupedMembers.length < index + 2) { - this.props.groupedMembers[index] = new Array(); + this.props.groupedMembers[index] = []; this.props.groupedMembers[index].push(docList[index]); } @@ -123,8 +123,7 @@ class PresentationViewList extends React.Component { export class PresentationView extends React.Component { public static Instance: PresentationView; - @observable groupedMembers: [Doc[]] = [[]]; - + @observable groupedMembers: Doc[][] = []; //observable means render is re-called every time variable is changed @observable -- cgit v1.2.3-70-g09d2 From 218c8b6476621ef0ffe151014f77bb1d506705a3 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Fri, 14 Jun 2019 18:24:01 -0400 Subject: Zooming added to presentation, and done --- src/client/util/DocumentManager.ts | 31 +++++++- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 5 +- src/client/views/SearchItem.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 6 +- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 2 + .../views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 +++++--- src/client/views/nodes/DocumentView.tsx | 10 ++- src/client/views/nodes/FieldView.tsx | 5 +- .../views/presentationview/PresentationElement.tsx | 31 +++++++- .../views/presentationview/PresentationView.tsx | 93 +++++++++++++++++++--- 14 files changed, 184 insertions(+), 42 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 65c4b9e4b..a613625cb 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { computed, observable } from 'mobx'; +import { computed, observable, action } from 'mobx'; import { DocumentView } from '../views/nodes/DocumentView'; import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types'; @@ -115,7 +115,7 @@ export class DocumentManager { } @undoBatch - public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise => { + public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise => { let doc = Doc.GetProto(docDelegate); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { @@ -128,7 +128,7 @@ export class DocumentManager { if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) { docView.props.Document.libraryBrush = true; if (linkPage !== undefined) docView.props.Document.curPage = linkPage; - docView.props.focus(docView.props.Document); + docView.props.focus(docView.props.Document, willZoom); } else { if (!contextDoc) { const actualDoc = Doc.MakeAlias(docDelegate); @@ -140,11 +140,34 @@ export class DocumentManager { docDelegate.libraryBrush = true; if (!forceDockFunc && (contextView = DocumentManager.Instance.getDocumentView(contextDoc))) { contextDoc.panTransformType = "Ease"; - contextView.props.focus(contextDoc); + contextView.props.focus(contextDoc, willZoom); } else { (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc); } } } } + + @action + zoomIntoScale = (docDelegate: Doc, scale: number) => { + let doc = Doc.GetProto(docDelegate); + + let docView: DocumentView | null; + docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.zoomToScale(scale); + } + } + + getScaleOfDocView = (docDelegate: Doc) => { + let doc = Doc.GetProto(docDelegate); + + let docView: DocumentView | null; + docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + return docView.props.getScale(); + } else { + return 1; + } + } } \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index f517f757a..34785446b 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -192,7 +192,7 @@ export class TooltipTextMenu { DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) { - DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false); } else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f); } diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 24327b995..d1224febe 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -102,6 +102,6 @@ export class MainOverlayTextBox extends React.Component ; } - else return (null); Z + else return (null); } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 3d772916e..f78879efe 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -199,7 +199,10 @@ export class MainView extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} - ContainingCollectionView={undefined} />; + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} + />; let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; console.log("GETTING mainContent()"); console.log(castRes instanceof Promise); diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 01c7316d6..101d893de 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -24,7 +24,7 @@ library.add(faFilm); export class SearchItem extends React.Component { onClick = () => { - DocumentManager.Instance.jumpToDocument(this.props.doc); + DocumentManager.Instance.jumpToDocument(this.props.doc, false); } //needs help diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index dcc1bd95d..81f574a6c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -9,7 +9,7 @@ import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnTrue, Utils } from "../../../Utils"; +import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; @@ -483,7 +483,9 @@ export class DockedFrameRenderer extends React.Component { whenActiveChanged={emptyFunction} focus={emptyFunction} addDocTab={this.addDocTab} - ContainingCollectionView={undefined} /> + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} /> ); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 11d71d023..715faafd0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -7,7 +7,7 @@ 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 { emptyFunction, returnFalse, returnZero } from "../../../Utils"; +import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; @@ -451,6 +451,8 @@ export class CollectionSchemaPreview extends React.Component )} {input} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index da7ea50c6..e1453c658 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -146,6 +146,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { bringToFront={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} collapseToPoint={this.collapseToPoint} + zoomToScale={emptyFunction} + getScale={returnOne} /> ); }) diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2814c0502..c80bd8fce 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -147,7 +147,7 @@ class TreeView extends React.Component { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document, false)) }); } ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5d26cb0c2..419d95b5f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -244,7 +244,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } - focusDocument = (doc: Doc) => { + focusDocument = (doc: Doc, willZoom: boolean) => { const panX = this.Document.panX; const panY = this.Document.panY; const id = this.Document[Id]; @@ -271,9 +271,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { newState.initializers[id] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); this.setPan(newPanX, newPanY); + this.props.Document.panTransformType = "Ease"; this.props.focus(this.props.Document); - //this.setScaleToZoom(doc); + if (willZoom) { + this.setScaleToZoom(doc); + } } @@ -281,21 +284,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let p = this.props; let PanelHeight = p.PanelHeight(); let panelWidth = p.PanelWidth(); - // let heightDif: number = PanelHeight - NumCast(doc.height); - // let widthDif: number = panelWidth - NumCast(doc.width); + let docHeight = NumCast(doc.height); let docWidth = NumCast(doc.width); - let targetHeight = 0.8 * PanelHeight; - let targetWidth = 0.8 * panelWidth; + let targetHeight = 0.5 * PanelHeight; + let targetWidth = 0.5 * panelWidth; let maxScaleX: number = targetWidth / docWidth; let maxScaleY: number = targetHeight / docHeight; - // let maxScaleX: number = NumCast(doc.width) / (panelWidth - 10); - // let maxScaleY: number = NumCast(doc.height) / (PanelHeight - 10); let maxApplicableScale = Math.min(maxScaleX, maxScaleY); this.Document.scale = maxApplicableScale; } + zoomToScale = (scale: number) => { + this.Document.scale = scale; + } + + getScale = () => { + if (this.Document.scale) { + return this.Document.scale; + } + return 1; + } + getDocumentViewProps(document: Doc): DocumentViewProps { return { @@ -315,6 +326,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, addDocTab: this.props.addDocTab, + zoomToScale: this.zoomToScale, + getScale: this.getScale }; } @@ -346,7 +359,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private childViews = () => [ , ...this.views - ]; + ] render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 449fa76a7..fb7657b68 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -69,13 +69,15 @@ export interface DocumentViewProps { ContentScaling: () => number; PanelWidth: () => number; PanelHeight: () => number; - focus: (doc: Doc) => void; + focus: (doc: Doc, willZoom: boolean) => void; selectOnLoad: boolean; parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; addDocTab: (doc: Doc, where: string) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; + zoomToScale: (scale: number) => void; + getScale: () => number; } const schema = createSchema({ @@ -302,8 +304,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); @@ -422,7 +424,7 @@ export class DocumentView extends DocComponent(Docu this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight"); }, icon: "search" }); - cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); + cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); 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" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 7b642b299..3047d55a3 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -103,7 +103,10 @@ export class FieldView extends React.Component { ContainingCollectionView={this.props.ContainingCollectionView} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} - bringToFront={emptyFunction} /> + bringToFront={emptyFunction} + zoomToScale={emptyFunction} + getScale={returnOne} + /> ); } else if (field instanceof List) { diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index c58570798..00dc07921 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -12,6 +12,7 @@ import { faFile as fileSolid, faLocationArrow, faArrowUp, faSearch } from '@fort import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; +import { DocumentManager } from "../../util/DocumentManager"; @@ -27,7 +28,7 @@ interface PresentationElementProps { document: Doc; index: number; deleteDocument(index: number): void; - gotoDocument(index: number): void; + gotoDocument(index: number, fromDoc: number): Promise; allListElements: Doc[]; groupMappings: Map; presStatus: boolean; @@ -300,6 +301,9 @@ export default class PresentationElement extends React.Component { + e.stopPropagation(); + if (this.selectedButtons[buttonIndex.Show]) { + this.selectedButtons[buttonIndex.Show] = false; + this.props.document.viewScale = 1; + + } else { + if (this.selectedButtons[buttonIndex.Navigate]) { + this.selectedButtons[buttonIndex.Navigate] = false; + } + this.selectedButtons[buttonIndex.Show] = true; + } + + this.autoSaveButtonChange(buttonIndex.Show); + + } + render() { let p = this.props; @@ -330,13 +355,13 @@ export default class PresentationElement extends React.Component { p.gotoDocument(p.index); e.stopPropagation(); }}> + onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}> {`${p.index + 1}. ${title}`}

- + diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx index d3365725b..6cf5fad7e 100644 --- a/src/client/views/presentationview/PresentationView.tsx +++ b/src/client/views/presentationview/PresentationView.tsx @@ -26,7 +26,7 @@ export interface PresViewProps { interface PresListProps extends PresViewProps { deleteDocument(index: number): void; - gotoDocument(index: number): void; + gotoDocument(index: number, fromDoc: number): Promise; groupMappings: Map; presElementsMappings: Map; setChildrenDocs: (docList: Doc[]) => void; @@ -71,9 +71,24 @@ class PresentationViewList extends React.Component { }); } + /** + * Initially every document starts with a viewScale 1, which means + * that they will be displayed in a canvas with scale 1. + */ + @action + initializeScaleViews = (docList: Doc[]) => { + docList.forEach((doc: Doc) => { + let curScale = NumCast(doc.viewScale, null); + if (curScale === undefined) { + doc.viewScale = 1; + } + }); + } + render() { const children = DocListCast(this.props.Document.data); this.initializeGroupIds(children); + this.initializeScaleViews(children); this.props.setChildrenDocs(children); return ( @@ -102,7 +117,6 @@ export class PresentationView extends React.Component { @observable presButtonBackUp: Doc = new Doc(); - componentDidMount() { //getting both backUp documents let castedGroupBackUp = Cast(this.props.Document.presGroupBackUp, Doc); @@ -195,7 +209,7 @@ export class PresentationView extends React.Component { if (nextSelected === current) nextSelected = current + 1; } - this.gotoDocument(nextSelected); + this.gotoDocument(nextSelected, current); } back = async () => { @@ -209,6 +223,7 @@ export class PresentationView extends React.Component { //asking for its presentation id. let curPresId = StrCast(docAtCurrent.presentId); let prevSelected = current - 1; + let zoomOut: boolean = false; //checking if this presentation id is mapped to a group, if so chosing the first element in group if (this.groupMappings.has(curPresId)) { @@ -217,11 +232,29 @@ export class PresentationView extends React.Component { //end of grup so go beyond if (prevSelected === current) prevSelected = current - 1; + //checking if any of the group members had used zooming in + currentsArray.forEach((doc: Doc) => { + if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) { + zoomOut = true; + return; + } + }); } + // if a group set that flag to zero or a single element + //If so making sure to zoom out, which goes back to state before zooming action + if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) { + let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null); + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]); + if (prevScale !== undefined) { + if (prevScale !== curScale) { + DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); + } + } + } + this.gotoDocument(prevSelected, current); - this.gotoDocument(prevSelected); } /** @@ -285,9 +318,10 @@ export class PresentationView extends React.Component { * has the option open and last in the group. If not in the group, and it has * te option open, navigates to that element. */ - navigateToElement = (curDoc: Doc) => { + navigateToElement = async (curDoc: Doc, fromDoc: number) => { let docToJump: Doc = curDoc; let curDocPresId = StrCast(curDoc.presentId, null); + let willZoom: boolean = false; //checking if in group if (curDocPresId !== undefined) { @@ -297,6 +331,11 @@ export class PresentationView extends React.Component { let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected; if (selectedButtons[buttonIndex.Navigate]) { docToJump = doc; + willZoom = false; + } + if (selectedButtons[buttonIndex.Show]) { + docToJump = doc; + willZoom = true; } }); } @@ -305,13 +344,35 @@ export class PresentationView extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - if (this.presElementsMappings.get(curDoc)!.selected[buttonIndex.Navigate]) { - DocumentManager.Instance.jumpToDocument(curDoc); - } else { - return; + let curDocButtons = this.presElementsMappings.get(curDoc)!.selected; + if (curDocButtons[buttonIndex.Navigate]) { + DocumentManager.Instance.jumpToDocument(curDoc, false); + } else if (curDocButtons[buttonIndex.Show]) { + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(curDoc, true); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + + //saving the scale user was on before zooming in + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; + } + } + return; + } + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(docToJump, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + //saving the scale that user was on + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; } - DocumentManager.Instance.jumpToDocument(docToJump); + } /** @@ -340,7 +401,7 @@ export class PresentationView extends React.Component { } } @action - public gotoDocument = async (index: number) => { + public gotoDocument = async (index: number, fromDoc: number) => { const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); if (!list) { return; @@ -357,7 +418,7 @@ export class PresentationView extends React.Component { const doc = await list[index]; if (this.presStatus) { - this.navigateToElement(doc); + this.navigateToElement(doc, fromDoc); this.hideIfNotPresented(index); this.showAfterPresented(index); } @@ -407,7 +468,8 @@ export class PresentationView extends React.Component { } else { this.presStatus = true; this.startPresentation(0); - this.gotoDocument(0); + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(0, current); } this.props.Document.presStatus = this.presStatus; } @@ -423,6 +485,11 @@ export class PresentationView extends React.Component { doc.opacity = 1; }); this.props.Document.selectedDoc = 0; + if (this.childrenDocs.length === 0) { + return; + } + DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1); + this.childrenDocs[0].viewScale = 1; } -- cgit v1.2.3-70-g09d2 From 135e252902a3ca93e95672602122afb3be6cd015 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 20 Jun 2019 10:43:58 -0400 Subject: basic pdf snippetting --- src/client/views/MainView.tsx | 3 ++- src/client/views/nodes/PDFBox.tsx | 20 ++++++++++++----- src/client/views/pdf/PDFMenu.tsx | 45 +++++++++++++++++++++++++++++++++++--- src/client/views/pdf/PDFViewer.tsx | 6 ++++- src/client/views/pdf/Page.tsx | 31 +++++++++++++++++++++----- 5 files changed, 89 insertions(+), 16 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e3d4ff8b5..2645e2789 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, faThumbtack, 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, faCut } 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'; @@ -91,6 +91,7 @@ export class MainView extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); + library.add(faCut); library.add(faCommentAlt); library.add(faThumbtack); this.initEventListeners(); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index d2de1cb1c..0aeb9afc8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -27,8 +27,22 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; + private _mainCont: React.RefObject; private _reactionDisposer?: IReactionDisposer; + constructor(props: FieldViewProps) { + super(props); + + this._mainCont = React.createRef(); + this._reactionDisposer = reaction( + () => this.props.Document.scrollY, + () => { + if (this._mainCont.current) { + this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + } + }); + } + componentDidMount() { if (this.props.setPdfBox) this.props.setPdfBox(this); } @@ -60,10 +74,6 @@ 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: "auto" }); - }); } loaded = (nw: number, nh: number, np: number) => { @@ -105,7 +115,7 @@ export class PDFBox extends DocComponent(PdfDocumen overflowY: "scroll", overflowX: "hidden", marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} - ref={this.createRef} + ref={this._mainCont} onWheel={(e: React.WheelEvent) => { e.stopPropagation(); }} className={classname}> diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 39b15fb11..a8e176858 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -5,6 +5,8 @@ import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { emptyFunction } from "../../../Utils"; import { Doc } from "../../../new_fields/Doc"; +import { DragManager } from "../../util/DragManager"; +import { DocUtils } from "../../documents/Documents"; @observer export default class PDFMenu extends React.Component { @@ -16,19 +18,23 @@ export default class PDFMenu extends React.Component { @observable private _transition: string = "opacity 0.5s"; @observable private _transitionDelay: string = ""; - @observable public Pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; Delete: () => void = emptyFunction; + Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; @observable public Highlighting: boolean = false; - @observable public Status: "pdf" | "annotation" | "" = ""; + @observable public Status: "pdf" | "annotation" | "snippet" | "" = ""; + @observable public Pinned: boolean = false; + + public Marquee: { left: number; top: number; width: number; height: number; } | undefined; private _offsetY: number = 0; private _offsetX: number = 0; private _mainCont: React.RefObject; private _dragging: boolean = false; + private _snippetButton: React.RefObject; constructor(props: Readonly<{}>) { super(props); @@ -36,6 +42,7 @@ export default class PDFMenu extends React.Component { PDFMenu.Instance = this; this._mainCont = React.createRef(); + this._snippetButton = React.createRef(); } pointerDown = (e: React.PointerEvent) => { @@ -171,13 +178,45 @@ export default class PDFMenu extends React.Component { e.preventDefault(); } + snippetStart = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.snippetDrag); + document.addEventListener("pointermove", this.snippetDrag); + document.removeEventListener("pointerup", this.snippetEnd); + document.addEventListener("pointerup", this.snippetEnd); + + e.stopPropagation(); + e.preventDefault(); + } + + snippetDrag = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + if (this._dragging) { + return; + } + this._dragging = true; + + if (this.Marquee) { + this.Snippet(this.Marquee); + } + } + + snippetEnd = (e: PointerEvent) => { + this._dragging = false; + document.removeEventListener("pointermove", this.snippetDrag); + document.removeEventListener("pointerup", this.snippetEnd); + e.stopPropagation(); + e.preventDefault(); + } + render() { - let buttons = this.Status === "pdf" ? [ + let buttons = this.Status === "pdf" || this.Status === "snippet" ? [ , , + this.Status === "snippet" ? : undefined, +
+
+ Annotation View Settings +
+
+ + +
+
+ +
+
+ +
+
+ + ); } loaded = (nw: number, nh: number, np: number) => { @@ -129,6 +207,7 @@ export class PDFBox extends DocComponent(PdfDocumen }} className={classname}> {/*
*/} + {this.settingsPanel()} ); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 75a8b042d..1fb208525 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -21,7 +21,7 @@ import React = require("react"); import PDFMenu from "./PDFMenu"; import { UndoManager } from "../../util/UndoManager"; import { ScriptField } from "../../../fields/ScriptField"; -import { CompileScript, CompiledScript } from "../../util/Scripting"; +import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting"; export const scale = 2; interface IPDFViewerProps { @@ -77,7 +77,7 @@ class Viewer extends React.Component { @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @observable private _savedAnnotations: Dictionary = new Dictionary(); - @observable private _script: ScriptField | undefined = this.props.parent.Document.filterScript; + @observable private _script: CompileResult | undefined; private _pageBuffer: number = 1; private _annotationLayer: React.RefObject = React.createRef(); @@ -86,6 +86,14 @@ class Viewer extends React.Component { private _dropDisposer?: DragManager.DragDropDisposer; private _filterReactionDisposer?: IReactionDisposer; + @action + constructor(props: IViewerProps) { + super(props); + + let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); + this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");; + } + componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY) { this.renderPages(); @@ -112,7 +120,10 @@ class Viewer extends React.Component { this._filterReactionDisposer = reaction( () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript, () => { - this._script = Cast(this.props.parent.Document.filterScript, ScriptField); + runInAction(() => { + let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); + this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");; + }); } ); } @@ -121,6 +132,7 @@ class Viewer extends React.Component { componentWillUnmount = () => { this._reactionDisposer && this._reactionDisposer(); this._annotationReactionDisposer && this._annotationReactionDisposer(); + this._filterReactionDisposer && this._filterReactionDisposer(); } @action @@ -159,10 +171,12 @@ class Viewer extends React.Component { makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; - let mainAnnoDoc = new Doc(); + let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {}); + + mainAnnoDoc.page = Math.round(Math.random()); this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { - let annoDoc = Docs.CreateInstance(new Doc(), this.props.parent.Document, {}); + let annoDoc = new Doc(); 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; @@ -360,7 +374,7 @@ class Viewer extends React.Component { } render() { - let compiled = this._script ? CompileScript(this._script.scriptString, { params: { this: Doc.name } }) : CompileScript("return true"); + let compiled = this._script; return (
@@ -372,7 +386,15 @@ class Viewer extends React.Component { pointerEvents: this.props.parent.props.active() ? "none" : "all" }}>
- {this._annotations.filter(anno => compiled.compiled ? compiled.run(anno) : true).map(anno => this.renderAnnotation(anno))} + {this._annotations.filter(anno => { + if (compiled && compiled.compiled) { + let run = compiled.run({ this: anno }); + if (run.success) { + return run.result; + } + } + return true; + }).map(anno => this.renderAnnotation(anno))}
-- cgit v1.2.3-70-g09d2 From 089aaf64964b0f1793a69ef6bf37eb2db41904af Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 24 Jun 2019 15:59:39 -0400 Subject: merge --- src/client/views/nodes/PDFBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index aa421ff9c..a129e89b9 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -125,7 +125,7 @@ export class PDFBox extends DocComponent(PdfDocumen return !this.props.active() ? (null) : (
e.stopPropagation()}> - , - , + , this.Status === "snippet" ? : undefined, - ] : [ - + , +
+ + +
, + , ]; return (
{buttons} - {/* - - */}
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1eab13bc5..3df7dd77b 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -20,8 +20,8 @@ import "./PDFViewer.scss"; import React = require("react"); import PDFMenu from "./PDFMenu"; import { UndoManager } from "../../util/UndoManager"; -import { ScriptField } from "../../../fields/ScriptField"; import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting"; +import { ScriptField } from "../../../new_fields/ScriptField"; export const scale = 2; interface IPDFViewerProps { @@ -63,8 +63,6 @@ interface IViewerProps { url: string; } -const PinRadius = 25; - /** * Handles rendering and virtualization of the pdf */ @@ -85,14 +83,18 @@ class Viewer extends React.Component { private _annotationReactionDisposer?: IReactionDisposer; private _dropDisposer?: DragManager.DragDropDisposer; private _filterReactionDisposer?: IReactionDisposer; + private _viewer: React.RefObject; + private _mainCont: React.RefObject; + private _textContent: Pdfjs.TextContent[] = []; - @action - constructor(props: IViewerProps) { - super(props); + constructor(props: IViewerProps) { + super(props); - let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); - this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");; - } + let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); + this._script = scriptfield ? scriptfield.script : CompileScript("return true"); + this._viewer = React.createRef(); + this._mainCont = React.createRef(); + } componentDidUpdate = (prevProps: IViewerProps) => { if (this.scrollY !== prevProps.scrollY) { @@ -118,21 +120,37 @@ class Viewer extends React.Component { if (this.props.parent.props.ContainingCollectionView) { this._filterReactionDisposer = reaction( - () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript, + () => this.props.parent.Document.filterScript, () => { runInAction(() => { let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField); - this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");; + this._script = scriptfield ? scriptfield.script : CompileScript("return true"); + if (this.props.parent.props.ContainingCollectionView) { + let ccvAnnos = DocListCast(this.props.parent.props.ContainingCollectionView.props.Document.annotations); + ccvAnnos.forEach(d => { + if (this._script && this._script.compiled) { + let run = this._script.run(d); + if (run.success) { + d.opacity = run.result ? 1 : 0; + } + } + }) + } }); } ); } + + if (this._mainCont.current) { + this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); + } } componentWillUnmount = () => { this._reactionDisposer && this._reactionDisposer(); this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); + this._dropDisposer && this._dropDisposer(); } @action @@ -140,10 +158,14 @@ class Viewer extends React.Component { if (this._pageSizes.length === 0) { let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); this._isPage = Array(this.props.pdf.numPages); + this._textContent = 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 }; let x = page.getViewport(scale); + page.getTextContent().then((text: Pdfjs.TextContent) => { + this._textContent[i] = text; + }) pageSizes[i] = { width: x.width, height: x.height }; })); } @@ -162,13 +184,6 @@ class Viewer extends React.Component { } } - private mainCont = (div: HTMLDivElement | null) => { - this._dropDisposer && this._dropDisposer(); - if (div) { - this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } }); - } - } - makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {}); @@ -222,6 +237,7 @@ class Viewer extends React.Component { pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => { this.props.loaded(page.width, page.height, this.props.pdf.numPages); } + @action getPlaceholderPage = (page: number) => { if (this._isPage[page] !== "none") { @@ -232,6 +248,7 @@ class Viewer extends React.Component { ); } } + @action getRenderedPage = (page: number) => { if (this._isPage[page] !== "page") { @@ -374,11 +391,16 @@ class Viewer extends React.Component { return res; } + pointerDown = () => { + + let x = this._textContent; + } + render() { let compiled = this._script; return ( -
-
+
+
{this._visibleElements}
{ } if (e.button === 2) { PDFMenu.Instance.Status = "annotation"; - PDFMenu.Instance.Delete = this.deleteAnnotation; + PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this); PDFMenu.Instance.Pinned = false; + PDFMenu.Instance.AddTag = this.addTag.bind(this); PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } } + addTag = (key: string, value: string): boolean => { + let group = FieldValue(Cast(this.props.document.group, Doc)); + if (group) { + let valNum = parseInt(value); + group[key] = isNaN(valNum) ? value : valNum; + return true; + } + return false; + } + render() { return (
Date: Wed, 26 Jun 2019 16:41:34 -0400 Subject: better search, prev/next annotations --- src/client/views/MainView.tsx | 4 +- src/client/views/nodes/PDFBox.tsx | 6 + src/client/views/pdf/PDFViewer.scss | 46 +++++- src/client/views/pdf/PDFViewer.tsx | 294 +++++++++++++++++++++++++++++------- 4 files changed, 290 insertions(+), 60 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 226eb458b..a9932feed 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, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut } 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'; @@ -95,6 +95,8 @@ export class MainView extends React.Component { library.add(faCommentAlt); library.add(faThumbtack); library.add(faCheck); + library.add(faArrowDown); + library.add(faArrowUp); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index c0f2d313a..44028ddf7 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -143,6 +143,12 @@ export class PDFBox extends DocComponent(PdfDocumen this.applyFilter(); } + scrollTo(y: number) { + if (this._mainCont.current) { + this._mainCont.current.scrollTo({ top: y }); + } + } + settingsPanel() { return !this.props.active() ? (null) : ( diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 2f705781f..5a89a85f4 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -53,14 +53,52 @@ justify-content: center; align-items: center; padding: 20px; + overflow: hidden; + transition: left .5s; +} + +.pdfViewer-overlaySearchBar { + width: 20%; + height: 100%; + font-size: 30px; + padding: 5px; +} + +.pdfViewer-overlayButton { + border-bottom-left-radius: 50%; + display: flex; + justify-content: space-evenly; + align-items: center; + height: 70px; + background: none; + padding: 0; + position: absolute; + + .pdfViewer-overlayButton-arrow { + width: 0; + height: 0; + border-top: 25px solid transparent; + border-bottom: 25px solid transparent; + border-right: 25px solid #121721; + transition: all 0.5s; + } - .pdfViewer-overlaySearchBar { - width: 20%; - height: 100%; - font-size: 30px; + .pdfViewer-overlayButton-iconCont { + background: #121721; + height: 50px; + width: 70px; + display: flex; + justify-content: center; + align-items: center; + margin-left: -2px; + border-radius: 3px; } } +.pdfViewer-overlayButton:hover { + background: none; +} + .pdfViewer-annotationBox { position: absolute; background-color: red; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 41961602d..d1d239f41 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -80,17 +80,23 @@ class Viewer extends React.Component { @observable private _script: CompileResult | undefined; @observable private _searching: boolean = false; + @observable public Index: number = -1; + private _pageBuffer: number = 1; private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _dropDisposer?: DragManager.DragDropDisposer; private _filterReactionDisposer?: IReactionDisposer; + private _activeReactionDisposer?: IReactionDisposer; private _viewer: React.RefObject; private _mainCont: React.RefObject; - private _textContent: Pdfjs.TextContent[] = []; + // private _textContent: Pdfjs.TextContent[] = []; private _pdfFindController: any; private _searchString: string = ""; + private _rendered: boolean = false; + private _pageIndex: number = -1; + private _matchIndex: number = 0; constructor(props: IViewerProps) { super(props); @@ -123,6 +129,24 @@ class Viewer extends React.Component { annotations && annotations.length && this.renderAnnotations(annotations, true), { fireImmediately: true }); + this._activeReactionDisposer = reaction( + () => this.props.parent.props.active(), + () => { + runInAction(() => { + if (!this.props.parent.props.active()) { + this._searching = false; + this._pdfFindController = null; + if (this._viewer.current) { + let cns = this._viewer.current.childNodes; + for (let i = cns.length - 1; i >= 0; i--) { + cns.item(i).remove(); + } + } + } + }); + } + ) + if (this.props.parent.props.ContainingCollectionView) { this._filterReactionDisposer = reaction( () => this.props.parent.Document.filterScript, @@ -158,24 +182,28 @@ class Viewer extends React.Component { this._dropDisposer && this._dropDisposer(); } + scrollTo(y: number) { + this.props.parent.scrollTo(y); + } + @action 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); - this._textContent = Array(this.props.pdf.numPages); + // this._textContent = 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 }; let x = page.getViewport(scale); - page.getTextContent().then((text: Pdfjs.TextContent) => { - // let tc = new Pdfjs.TextContentItem() - // let tc = {str: } - this._textContent[i] = text; - // text.items.forEach(t => { - // tcStr += t.str; - // }) - }); + // page.getTextContent().then((text: Pdfjs.TextContent) => { + // // let tc = new Pdfjs.TextContentItem() + // // let tc = {str: } + // this._textContent[i] = text; + // // text.items.forEach(t => { + // // tcStr += t.str; + // // }) + // }); pageSizes[i] = { width: x.width, height: x.height }; })); } @@ -385,7 +413,7 @@ class Viewer extends React.Component { } } - renderAnnotation = (anno: Doc): JSX.Element[] => { + renderAnnotation = (anno: Doc, index: number): JSX.Element[] => { let annotationDocs = DocListCast(anno.annotations); let res = annotationDocs.map(a => { let type = NumCast(a.type); @@ -393,7 +421,7 @@ class Viewer extends React.Component { // case AnnotationTypes.Pin: // return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } @@ -403,14 +431,7 @@ class Viewer extends React.Component { @action pointerDown = () => { - this._searching = false; - this._pdfFindController = null; - if (this._viewer.current) { - let cns = this._viewer.current.childNodes; - for (let i = cns.length - 1; i >= 0; i--) { - cns.item(i).remove(); - } - } + // this._searching = false; } @action @@ -418,23 +439,20 @@ class Viewer extends React.Component { if (searchString.length === 0) { return; } - this._searching = true; - - let container = this._mainCont.current; - let viewer = this._viewer.current; - - if (!this._pdfFindController) { - if (container && viewer) { - let simpleLinkService = new SimpleLinkService(); - let pdfViewer = new PDFJSViewer.PDFViewer({ - container: container, - viewer: viewer, - linkService: simpleLinkService - }); - simpleLinkService.setPdf(this.props.pdf); - container.addEventListener("pagesinit", () => { - pdfViewer.currentScaleValue = 1; + + if (this._rendered) { + this._pdfFindController.executeCommand('find', + { + caseSensitive: false, + findPrevious: undefined, + highlightAll: true, + phraseSearch: true, + query: searchString }); + } + else { + let container = this._mainCont.current; + if (container) { container.addEventListener("pagerendered", () => { console.log("rendered"); this._pdfFindController.executeCommand('find', @@ -445,29 +463,151 @@ class Viewer extends React.Component { phraseSearch: true, query: searchString }); + this._rendered = true; }); - pdfViewer.setDocument(this.props.pdf); - this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer); - // this._pdfFindController._linkService = pdfLinkService; - pdfViewer.findController = this._pdfFindController; } } - else { - this._pdfFindController.executeCommand('find', - { - caseSensitive: false, - findPrevious: undefined, - highlightAll: true, - phraseSearch: true, - query: searchString - }); - } + + // let viewer = this._viewer.current; + + // if (!this._pdfFindController) { + // if (container && viewer) { + // let simpleLinkService = new SimpleLinkService(); + // let pdfViewer = new PDFJSViewer.PDFViewer({ + // container: container, + // viewer: viewer, + // linkService: simpleLinkService + // }); + // simpleLinkService.setPdf(this.props.pdf); + // container.addEventListener("pagesinit", () => { + // pdfViewer.currentScaleValue = 1; + // }); + // container.addEventListener("pagerendered", () => { + // console.log("rendered"); + // this._pdfFindController.executeCommand('find', + // { + // caseSensitive: false, + // findPrevious: undefined, + // highlightAll: true, + // phraseSearch: true, + // query: searchString + // }); + // }); + // pdfViewer.setDocument(this.props.pdf); + // this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer); + // // this._pdfFindController._linkService = pdfLinkService; + // pdfViewer.findController = this._pdfFindController; + // } + // } + // else { + // this._pdfFindController.executeCommand('find', + // { + // caseSensitive: false, + // findPrevious: undefined, + // highlightAll: true, + // phraseSearch: true, + // query: searchString + // }); + // } } searchStringChanged = (e: React.ChangeEvent) => { this._searchString = e.currentTarget.value; } + @action + toggleSearch = (e: React.MouseEvent) => { + e.stopPropagation(); + this._searching = !this._searching; + + if (this._searching) { + let container = this._mainCont.current; + let viewer = this._viewer.current; + + if (!this._pdfFindController) { + if (container && viewer) { + let simpleLinkService = new SimpleLinkService(); + let pdfViewer = new PDFJSViewer.PDFViewer({ + container: container, + viewer: viewer, + linkService: simpleLinkService + }); + simpleLinkService.setPdf(this.props.pdf); + container.addEventListener("pagesinit", () => { + pdfViewer.currentScaleValue = 1; + }); + container.addEventListener("pagerendered", () => { + console.log("rendered"); + this._rendered = true; + }); + pdfViewer.setDocument(this.props.pdf); + this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer); + // this._pdfFindController._linkService = pdfLinkService; + pdfViewer.findController = this._pdfFindController; + } + } + } + else { + this._pdfFindController = null; + if (this._viewer.current) { + let cns = this._viewer.current.childNodes; + for (let i = cns.length - 1; i >= 0; i--) { + cns.item(i).remove(); + } + } + } + } + + @action + prevAnnotation = (e: React.MouseEvent) => { + e.stopPropagation(); + + if (this.Index > 0) { + this.Index--; + } + } + + @action + nextAnnotation = (e: React.MouseEvent) => { + e.stopPropagation(); + + let compiled = this._script; + if (this.Index < this._annotations.filter(anno => { + if (compiled && compiled.compiled) { + let run = compiled.run({ this: anno }); + if (run.success) { + return run.result; + } + } + return true; + }).length) { + this.Index++; + } + } + + nextResult = () => { + // if (this._viewer.current) { + // let results = this._pdfFindController.pageMatches; + // if (results && results.length) { + // if (this._pageIndex === this.props.pdf.numPages && this._matchIndex === results[this._pageIndex].length - 1) { + // return; + // } + // if (this._pageIndex === -1 || this._matchIndex === results[this._pageIndex].length - 1) { + // this._matchIndex = 0; + // this._pageIndex++; + // } + // else { + // this._matchIndex++; + // } + // this._pdfFindController._nextMatch() + // let nextMatch = this._viewer.current.children[this._pageIndex].children[1].children[results[this._pageIndex][this._matchIndex]]; + // rconsole.log(nextMatch); + // this.props.parent.scrollTo(nextMatch.getBoundingClientRect().top); + // nextMatch.setAttribute("style", nextMatch.getAttribute("style") ? nextMatch.getAttribute("style") + ", background-color: green" : "background-color: green"); + // } + // } + } + render() { let compiled = this._script; return ( @@ -490,13 +630,39 @@ class Viewer extends React.Component { } } return true; - }).map(anno => this.renderAnnotation(anno))} + }).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
-
e.stopPropagation()}> +
e.stopPropagation()} + style={{ + bottom: -this.props.scrollY, + left: `${this._searching ? 0 : 100}%` + }}> + + {/* + */}
+ + +
); } @@ -511,14 +677,17 @@ interface IAnnotationProps { y: number; width: number; height: number; + index: number; parent: Viewer; document: Doc; } +@observer class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; private _reactionDisposer?: IReactionDisposer; + private _scrollDisposer?: IReactionDisposer; private _mainCont: React.RefObject; constructor(props: IAnnotationProps) { @@ -539,10 +708,20 @@ class RegionAnnotation extends React.Component { }, { fireImmediately: true } ); + + this._scrollDisposer = reaction( + () => this.props.parent.Index, + () => { + if (this.props.parent.Index === this.props.index) { + this.props.parent.scrollTo(this.props.y - 50); + } + } + ) } componentWillUnmount() { this._reactionDisposer && this._reactionDisposer(); + this._scrollDisposer && this._scrollDisposer(); } deleteAnnotation = () => { @@ -591,7 +770,14 @@ 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: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color) + }}>
); } } @@ -601,8 +787,6 @@ class SimpleLinkService { externalLinkRel: any = null; pdf: any = null; - constructor() { } - navigateTo(dest: any) { } getDestinationHash(dest: any) { return "#"; } -- cgit v1.2.3-70-g09d2