From faaff9fe8aab3bd5d116eab8bd85198a0756fe30 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 1 Oct 2019 10:12:50 -0400 Subject: fixed issues with pdf loading and layout. --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 13fd8ea98..3bf53340b 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -628,7 +628,7 @@ export class PDFViewer extends React.Component { } let nativeWidth = NumCast(this.props.Document.nativeWidth); let nativeHeight = NumCast(this.props.Document.nativeHeight); - return this._showWaiting = false)} + return this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)} style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; } -- cgit v1.2.3-70-g09d2 From 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 1 Oct 2019 15:10:22 -0400 Subject: fixed a bunch of pdf related things with stacking views. --- .../views/collections/CollectionDockingView.tsx | 9 ++-- .../views/collections/CollectionSchemaView.tsx | 6 +-- .../views/collections/CollectionStackingView.tsx | 6 +-- src/client/views/nodes/DocumentView.tsx | 54 ++++++++++++++++------ src/client/views/nodes/PDFBox.tsx | 16 +++++-- src/client/views/pdf/PDFViewer.tsx | 17 +++++-- src/new_fields/Doc.ts | 1 + 7 files changed, 78 insertions(+), 31 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 246546c9e..98aff41d3 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -3,7 +3,7 @@ import { faFile } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, Lambda, observable, reaction, computed, runInAction } from "mobx"; +import { action, Lambda, observable, reaction, computed, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; @@ -659,7 +659,10 @@ export class DockedFrameRenderer extends React.Component { return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc); } } - docView(document: Doc) { + + @computed get docView() { + if (!this._document) return (null); + const document = this._document; let resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc; return { transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, height: this._document && this._document.fitWidth ? undefined : "100%" }}> - {this.docView(this._document)} + {this.docView} ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 853808335..24f585a07 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -958,10 +958,10 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); - private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); + private PanelWidth = () => this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); + private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); - get centeringOffset() { return this.nativeWidth ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; } + get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { this.props.setPreviewScript(e.currentTarget.value); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e2020601b..4b81db3a6 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CursorProperty } from "csstype"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import Switch from 'rc-switch'; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; @@ -133,7 +133,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); - return doc) { if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid); return wid * aspect; } - return d.fitWidth ? Math.min(this.props.PanelHeight() - 2 * this.yMargin, d[HeightSym]()) : d[HeightSym](); + return d.fitWidth ? this.props.PanelHeight() - 2 * this.yMargin : d[HeightSym](); } columnDividerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 779e701ea..ded50890d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; -import { action, computed, runInAction } from "mobx"; +import { action, computed, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc"; @@ -601,6 +601,39 @@ export class DocumentView extends DocComponent(Docu return (showTitle ? 25 : 0) + 1; } + childScaling = () => (this.props.Document.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + @computed get contents() { + return (); + } render() { const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined; const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; @@ -610,8 +643,8 @@ export class DocumentView extends DocComponent(Docu this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) : ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); - const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.Document.ignoreAspect || this.props.Document.fitWidth ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "100%"; + const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; + const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() : this.Document.ignoreAspect || this.props.Document.fitWidth ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); @@ -645,13 +678,6 @@ export class DocumentView extends DocComponent(Docu SetValue={(value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true} /> ); - const contents = (); return (
(Docu background: backgroundColor, width: nativeWidth, height: nativeHeight, - transform: `scale(${this.props.ContentScaling()})`, + transform: `scale(${this.props.Document.fitWidth ? 1 : this.props.ContentScaling()})`, opacity: this.Document.opacity }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} @@ -675,15 +701,15 @@ export class DocumentView extends DocComponent(Docu {!showTitle && !showCaption ? this.Document.searchFields ? (
- {contents} + {this.contents} {searchHighlight}
) : - contents + this.contents :
- {contents} + {this.contents}
{titleView} {captionView} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3014af944..3ad0b4010 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -35,6 +35,7 @@ export class PDFBox extends DocComponent(PdfDocumen private _scriptValue: string = ""; private _searchString: string = ""; private _isChildActive = false; + private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title private _pdfViewer: PDFViewer | undefined; private _keyRef: React.RefObject = React.createRef(); private _valueRef: React.RefObject = React.createRef(); @@ -176,20 +177,25 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } - render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); - return (!(pdfUrl instanceof PdfField) || !this._pdf || this.props.ScreenToLocalTransform().Scale > 6 ? + let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; + if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true; + return (!this._everActive ?
{` ${this.props.Document.title}`}
: -
{ +
{ let hit = document.elementFromPoint(e.clientX, e.clientY); if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation e.button === 0 && e.stopPropagation(); } }}> - (PdfDocumen pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} /> + fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this.props.ScreenToLocalTransform().Scale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 3bf53340b..91a8cbf9f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -41,6 +41,7 @@ interface IViewerProps { PanelHeight: () => number; ContentScaling: () => number; select: (isCtrlPressed: boolean) => void; + startupLive: boolean; renderDepth: number; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; @@ -75,6 +76,7 @@ export class PDFViewer extends React.Component { @observable private _zoomed = 1; public pdfViewer: any; + private _retries = 0; // number of times tried to create the PDF viewer private _isChildActive = false; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); @@ -84,7 +86,6 @@ export class PDFViewer extends React.Component { private _filterReactionDisposer?: IReactionDisposer; private _viewer: React.RefObject = React.createRef(); private _mainCont: React.RefObject = React.createRef(); - private _marquee: React.RefObject = React.createRef(); private _selectionText: string = ""; private _startX: number = 0; private _startY: number = 0; @@ -106,7 +107,7 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.setupPdfJsViewer()); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -199,6 +200,17 @@ export class PDFViewer extends React.Component { this.gotoPage(NumCast(this.props.Document.curPage, 1)); })); document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); + this.createPdfViewer(); + } + + createPdfViewer() { + if (!this._mainCont.current) { + if (this._retries < 5) { + this._retries++; + setTimeout(() => this.createPdfViewer(), 1000); + } + return; + } var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -664,7 +676,6 @@ export class PDFViewer extends React.Component { marqueeY = () => this._marqueeY; marqueeing = () => this._marqueeing; render() { - trace(); return (
{this.pdfViewerDiv} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 4a03fed08..a68692732 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -712,6 +712,7 @@ export namespace Doc { } Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); +Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); Scripting.addGlobal(function aliasDocs(field: any) { return new List(field.map((d: any) => Doc.MakeAlias(d))); }); Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 88b20ece053ae85e541f4903748ee311b9ecf81c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 1 Oct 2019 21:48:15 -0400 Subject: better fix than last one for icon sizes and default pdf dimensions. --- src/client/documents/Documents.ts | 1 + src/client/views/nodes/IconBox.tsx | 6 +++--- src/client/views/nodes/PDFBox.scss | 2 +- src/client/views/nodes/PDFBox.tsx | 6 +++--- src/client/views/pdf/PDFViewer.tsx | 5 +++-- 5 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0d04d044e..4d9698532 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -591,6 +591,7 @@ export namespace Docs { if (type.indexOf("pdf") !== -1) { ctor = Docs.Create.PdfDocument; options.nativeWidth = 1200; + options.nativeHeight = 1200; } if (type.indexOf("excel") !== -1) { ctor = Docs.Create.DBDocument; diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 63a504d1a..f3adade58 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -77,9 +77,9 @@ export class IconBox extends React.Component {
{this.minimizedIcon} runInAction(() => { - if (r.offset!.width || BoolCast(this.props.Document.hideLabel)) { - this.props.Document.nativeWidth = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); - if (this.props.Document.height === Number(MINIMIZED_ICON_SIZE)) this.props.Document.width = this.props.Document.nativeWidth; + if (r.offset!.width || this.props.Document.hideLabel) { + this.props.Document.iconWidth = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); + if (this.props.Document.height === Number(MINIMIZED_ICON_SIZE)) this.props.Document.width = this.props.Document.iconWidth; } })}> {({ measureRef }) => diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 6393f21f7..1c1d6ec95 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -10,7 +10,7 @@ } .pdfBox-title-outer { - + z-index: 0; position: absolute; width: 100%; height: 100%; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index a3b375be7..617b580dd 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -183,15 +183,15 @@ export class PDFBox extends DocComponent(PdfDocumen let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true; return (!this._everActive ? -
-
+
+
{` ${this.props.Document.title}`}
:
{ let hit = document.elementFromPoint(e.clientX, e.clientY); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 91a8cbf9f..33226eac4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -24,6 +24,7 @@ import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { SelectionManager } from "../../util/SelectionManager"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -107,7 +108,7 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1 && this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -632,7 +633,7 @@ export class PDFViewer extends React.Component { } getCoverImage = () => { - if (!this.props.Document[HeightSym]()) { + if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { setTimeout(() => { this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width; -- cgit v1.2.3-70-g09d2 From 9427474b473d70974784a1517a1be902fb8d18ee Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 2 Oct 2019 18:26:55 -0400 Subject: many fixes to pdfs, linking, annotations, presentations. --- src/client/documents/Documents.ts | 24 ++-- src/client/util/DocumentManager.ts | 125 +++++++++++---------- src/client/util/RichTextSchema.tsx | 9 +- src/client/util/SharingManager.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 9 +- .../views/collections/CollectionDockingView.tsx | 10 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- src/client/views/linking/LinkFollowBox.tsx | 11 +- src/client/views/nodes/DocumentView.tsx | 44 ++------ src/client/views/nodes/FormattedTextBox.tsx | 79 ++++++------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 17 ++- src/client/views/nodes/PresBox.tsx | 24 ++-- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 17 ++- src/client/views/pdf/PDFMenu.tsx | 6 +- src/client/views/pdf/PDFViewer.tsx | 72 +++++------- .../views/presentationview/PresElementBox.tsx | 32 +++--- src/new_fields/Doc.ts | 2 + 21 files changed, 224 insertions(+), 276 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4d9698532..2d323ea4b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -653,32 +653,30 @@ export namespace DocUtils { } }); } - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) { - let sv = DocumentManager.Instance.getDocumentView(source); - if (sv && sv.props.ContainingCollectionDoc === target) return; - if (target === CurrentUserUtils.UserDocument) return undefined; + export function MakeLink(source: {doc:Doc,ctx?:Doc}, target: {doc:Doc,ctx?:Doc}, title: string = "", description: string = "", id?: string, anchored1?: boolean) { + let sv = DocumentManager.Instance.getDocumentView(source.doc); + if (sv && sv.props.ContainingCollectionDoc === target.doc) return; + if (target.doc === CurrentUserUtils.UserDocument) return undefined; let linkDocProto = new Doc(id, true); UndoManager.RunInBatch(() => { linkDocProto.type = DocumentType.LINK; - linkDocProto.targetContext = targetContext; - linkDocProto.sourceContext = sourceContext; - linkDocProto.title = title === "" ? source.title + " to " + target.title : title; + linkDocProto.targetContext = target.ctx; + linkDocProto.sourceContext = source.ctx; + linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; linkDocProto.linkDescription = description; - linkDocProto.anchor1 = source; - linkDocProto.anchor1Page = source.curPage; + linkDocProto.anchor1 = source.doc; linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor1anchored = anchored1; - linkDocProto.anchor2 = target; - linkDocProto.anchor2Page = target.curPage; + linkDocProto.anchor2 = target.doc; linkDocProto.anchor2Groups = new List([]); LinkManager.Instance.addLink(linkDocProto); - Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); }, "make link"); return linkDocProto; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4e6968f08..4ebcdf83c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ import { action, computed, observable } from 'mobx'; import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; -import { Cast, NumCast } from '../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; @@ -56,9 +56,9 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { - let toReturn: DocumentView | null = null; + let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; for (let pass of passes) { @@ -81,10 +81,14 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } + public getFirstDocumentView(toFind: Doc): DocumentView | undefined { + const views = this.getDocumentViews(toFind); + return views.length ? views[0] : undefined; + } public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; @@ -127,72 +131,70 @@ export class DocumentManager { return pairs; } - - @undoBatch - public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise => { - let doc = Doc.GetProto(docDelegate); - const contextDoc = await Cast(doc.annotationOn, Doc); - if (contextDoc) { - contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2 * 72 / 96; - } - - let docView: DocumentView | null; - // using forceDockFunc as a flag for splitting linked to doc to the right...can change later if needed - if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) { - Doc.BrushDoc(docView.props.Document); - if (linkPage !== undefined) docView.props.Document.curPage = linkPage; - UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, closeContextIfNotFound: boolean = false): Promise => { + const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc); + const annotatedDoc = await Cast(targetDoc.annotationOn, Doc); + if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? + annotatedDoc && docView.props.focus(annotatedDoc, false); + docView.props.focus(targetDoc, willZoom); } else { - if (!contextDoc) { - let docs = docContext ? await DocListCastAsync(docContext.data) : undefined; - let found = false; - // bcz: this just searches within the context for the target -- perhaps it should recursively search through all children? - docs && docs.map(d => found = found || Doc.AreProtosEqual(d, docDelegate)); - if (docContext && found) { - let targetContextView: DocumentView | null; - - if (!forceDockFunc && docContext && (targetContextView = DocumentManager.Instance.getDocumentView(docContext))) { - docContext.panTransformType = "Ease"; - targetContextView.props.focus(docDelegate, willZoom); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(docContext, undefined); - setTimeout(() => { - let dv = DocumentManager.Instance.getDocumentView(docContext); - dv && this.jumpToDocument(docDelegate, willZoom, forceDockFunc, - doc => dv!.props.focus(dv!.props.Document, true, 1, () => dv!.props.addDocTab(doc, undefined, "inPlace")), - linkPage); - }, 1050); - } - } else { - const actualDoc = Doc.MakeAlias(docDelegate); - Doc.BrushDoc(actualDoc); - if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc, undefined); - } + const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; + const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; + const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc); + + if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); } else { - let contextView: DocumentView | null; - Doc.BrushDoc(docDelegate); - let contextViews = DocumentManager.Instance.getDocumentViews(contextDoc); - if (!forceDockFunc && contextViews.length) { - contextView = contextViews[0]; - SelectionManager.SelectDoc(contextView, false); // force unrendered annotations to be created - contextDoc.panTransformType = "Ease"; + const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext); + if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context + targetDocContext.panTransformType = "Ease"; + targetDocContext.scrollY = 0; + targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); + + // now find the target document within the context setTimeout(() => { - SelectionManager.DeselectDoc(contextView!); - docView = DocumentManager.Instance.getDocumentView(docDelegate); - docView && contextView!.props.focus(contextView!.props.Document, willZoom); - docView && UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc); + if (retryDocView) { + retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context + } else { + if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document); + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target + } }, 0); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined); + } else { // there's no context view so we need to create one first and try again + targetDocContext.scrollY = 0; + (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined); setTimeout(() => { - this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage); - }, 1000); + const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext); + if (foundTargetDocContextView) { // we should always find a target context here.... + this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, true); // so call jump to doc again and if the doc isn't found, it will be created. + } + }, 2000); // the long timeout gives the context view a chance to create its children. think pdf's which need to be activated to render their annotations. } } } } + public async FollowLink(doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) { + let linkDocs = LinkManager.Instance.getAllRelatedLinks(doc); + SelectionManager.DeselectAll(); + let firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored); + let secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored); + const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); + let first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs; + let second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs; + let linkFollowDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor2 as Doc] : undefined; + let linkFollowDocContexts = first.length ? [await (first[0].targetContext) as Doc, await (first[0].sourceContext) as Doc] : second.length ? [await (second[0].sourceContext) as Doc, await (second[0].targetContext) as Doc] : [undefined, undefined]; + if (linkFollowDocs && !linkFollowDocs.some(l => l instanceof Promise)) { + let maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab"); + let targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined; + DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, + // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards + (doc: Doc) => focus(doc, maxLocation), targetContext); + } + } + @action zoomIntoScale = (docDelegate: Doc, scale: number) => { let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate)); @@ -202,8 +204,7 @@ export class DocumentManager { getScaleOfDocView = (docDelegate: Doc) => { let doc = Doc.GetProto(docDelegate); - let docView: DocumentView | null; - docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { return docView.props.getScale(); } else { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 64821d8db..49bd93942 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -627,17 +627,16 @@ export class ImageResizeView { let jumpToDoc = await Cast(linkDoc.anchor2, Doc); if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext) { - DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index f427e40b1..1cde2aa8e 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -192,7 +192,7 @@ export default class SharingManager extends React.Component<{}> { onClick={() => { let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { - DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, undefined, undefined, context.props.Document); + DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } }} onPointerEnter={action(() => { diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index e928887e2..38d050e5c 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -5,7 +5,7 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from '../../../new_fields/Types'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { ContextMenu } from '../ContextMenu'; @@ -130,8 +130,11 @@ export class CollectionBaseView extends React.Component { let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => - annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined)); + PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => { + if (Doc.AreProtosEqual(annotationOn, FieldValue(Cast(this.dataDoc.extendsDoc, Doc)))) { + Doc.GetProto(doc).annotationOn = undefined; + } + }); if (index !== -1) { value.splice(index, 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 98aff41d3..14e513157 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -574,8 +574,8 @@ export class DockedFrameRenderer extends React.Component { let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; if (curPres) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = doc; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = doc; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); const data = Cast(curPres.data, listSpec(Doc)); if (data) { data.push(pinDoc); @@ -614,8 +614,8 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); + panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth; + panelHeight = () => this._panelHeight; nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; @@ -644,7 +644,7 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; } + get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { SelectionManager.DeselectAll(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 155f2718b..28d1eb384 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -31,6 +31,7 @@ export interface CollectionViewProps extends FieldViewProps { moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; + VisibleHeight?: () => number; chromeCollapsed: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ffd1f9170..882a0f144 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -217,7 +217,7 @@ class TreeView extends React.Component { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.document; - DocUtils.MakeLink(sourceDoc, destDoc); + DocUtils.MakeLink({doc:sourceDoc}, {doc:destDoc}); e.stopPropagation(); } if (de.data instanceof DragManager.DocumentDragData) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eb40e0bcb..4bdede48a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -403,8 +403,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { SelectionManager.DeselectAll(); if (this.props.Document.scrollHeight) { let annotOn = Cast(doc.annotationOn, Doc) as Doc; - let offset = annotOn && (NumCast(annotOn.height) / 2 * 72 / 96); - this.props.Document.scrollY = NumCast(doc.y) - offset; + if (!annotOn) { + this.props.focus(doc); + } else { + let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height); + let offset = annotOn && (contextHgt / 2 * 96 / 72); + this.props.Document.scrollY = NumCast(doc.y) - offset; + } } else { const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; @@ -416,6 +421,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.setPan(newPanX, newPanY); this.Document.panTransformType = "Ease"; + Doc.BrushDoc(this.props.Document); this.props.focus(this.props.Document); willZoom && this.setScaleToZoom(doc, scale); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 72fff8e53..53b720a9e 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component { @undoBatch openFullScreen = () => { if (LinkFollowBox.destinationDoc) { - let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); + let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); } } @@ -250,10 +250,10 @@ export class LinkFollowBox extends React.Component { let dockingFunc = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, async document => dockingFunc(document), targetContext); } else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { let views = DocumentManager.Instance.getDocumentViews(jumpToDoc); @@ -264,12 +264,11 @@ export class LinkFollowBox extends React.Component { } } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined, - NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom); } else { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, dockingFunc); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, dockingFunc); } this.highlightDoc(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a903f8fe2..3273abc1d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -206,7 +206,7 @@ export class DocumentView extends DocComponent(Docu buttonClick = async (altKey: boolean, ctrlKey: boolean) => { let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); + let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; @@ -223,28 +223,10 @@ export class DocumentView extends DocComponent(Docu expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation))); } } - else if (linkedDocs.length) { - SelectionManager.DeselectAll(); - let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored); - let second = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor2 as Doc, this.props.Document) && !d.anchor2anchored); - let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); - let secondUnshown = second.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); - if (firstUnshown.length) first = [firstUnshown[0]]; - if (secondUnshown.length) second = [secondUnshown[0]]; - let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor1 as Doc] : undefined; - - // @TODO: shouldn't always follow target context - let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined]; - let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined]; - - if (linkedFwdDocs && !linkedFwdDocs.some(l => l instanceof Promise)) { - let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab"); - let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, - // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards - doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), - linkedFwdPage[altKey ? 1 : 0], targetContext); - } + else if (linkDocs.length) { + DocumentManager.Instance.FollowLink(this.props.Document, + (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), + ctrlKey, altKey, this.props.ContainingCollectionDoc); } } @@ -352,15 +334,9 @@ export class DocumentView extends DocComponent(Docu if (de.data instanceof DragManager.AnnotationDragData) { /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner e.stopPropagation(); - let sourceDoc = de.data.annotationDocument; - let targetDoc = this.props.Document; - let annotations = await DocListCastAsync(sourceDoc.annotations); - sourceDoc.linkedToDoc = true; - de.data.targetContext = this.props.ContainingCollectionDoc; - targetDoc.targetContext = de.data.targetContext; - annotations && annotations.forEach(anno => anno.target = targetDoc); - - DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`); + (de.data as any).linkedToDoc = true; + + DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`); } if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document); @@ -371,7 +347,7 @@ export class DocumentView extends DocComponent(Docu // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); de.data.linkSourceDocument !== this.props.Document && - (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed + (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed } } @@ -407,7 +383,7 @@ export class DocumentView extends DocComponent(Docu let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); DocServer.GetRefField(portalID).then(existingPortal => { let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID }); - DocUtils.MakeLink(this.props.Document, portal, undefined, portalID); + DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link"); this.Document.isButton = true; }); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 449f56b16..749886d9a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -9,7 +9,7 @@ import { Fragment, Node, Node as ProsNode, NodeType, Slice, Mark, ResolvedPos } import { EditorState, Plugin, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { Copy, Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { RichTextField } from "../../../new_fields/RichTextField"; @@ -230,7 +230,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } - else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true); + else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id, true); }); }); }); @@ -342,7 +342,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image; let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y }); this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] }))); - DocUtils.MakeLink(this.dataDoc, target, undefined, "ImgRef:" + target.title, undefined, undefined, target[Id]); + DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title); this.tryUpdateHeight(); e.stopPropagation(); } else if (de.data instanceof DragManager.DocumentDragData) { @@ -667,55 +667,42 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => { let cbe = event as ClipboardEvent; - let docId: string; - let regionId: string; - if (!cbe.clipboardData) { - return false; - } - let linkId: string; - docId = cbe.clipboardData.getData("dash/pdfOrigin"); - regionId = cbe.clipboardData.getData("dash/pdfRegion"); - if (!docId || !regionId) { - return false; - } - - DocServer.GetRefField(docId).then(doc => { - DocServer.GetRefField(regionId).then(region => { - if (!(doc instanceof Doc) || !(region instanceof Doc)) { - return; - } - - let annotations = DocListCast(region.annotations); - annotations.forEach(anno => anno.target = this.props.Document); - let fieldExtDoc = Doc.fieldExtensionDoc(doc, "data"); - let targetAnnotations = DocListCast(fieldExtDoc.annotations); - if (targetAnnotations) { - targetAnnotations.push(region); - fieldExtDoc.annotations = new List(targetAnnotations); - } + const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin"); + const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion"); + if (pdfDocId && pdfRegionId) { + DocServer.GetRefField(pdfDocId).then(pdfDoc => { + DocServer.GetRefField(pdfRegionId).then(pdfRegion => { + if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) { + setTimeout(async () => { + let targetAnnotations = await DocListCastAsync(Doc.fieldExtensionDoc(pdfDoc, "data").annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations + targetAnnotations && targetAnnotations.push(pdfRegion); + }); - let link = DocUtils.MakeLink(this.props.Document, region, doc); - if (link) { - cbe.clipboardData!.setData("dash/linkDoc", link[Id]); - linkId = link[Id]; - let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title))); - slice = new Slice(frag, slice.openStart, slice.openEnd); - var tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); - } + let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link"); + if (link) { + cbe.clipboardData!.setData("dash/linkDoc", link[Id]); + let linkId = link[Id]; + let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)); + slice = new Slice(frag, slice.openStart, slice.openEnd); + var tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); + } + } + }); }); - }); + return true; + } + return false; - return true; function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) { const nodes: Node[] = []; frag.forEach(node => nodes.push(marker(node))); return Fragment.fromArray(nodes); } - function addLinkMark(node: Node, title: string) { + function addLinkMark(node: Node, title: string, linkId: string) { if (!node.isText) { - const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title)); + const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId)); return node.copy(content); } const marks = [...node.marks]; @@ -879,16 +866,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), targetContext); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9e9fe1324..fe4f75cad 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -299,7 +299,7 @@ export class ImageBox extends DocComponent(ImageD let rotation = NumCast(this.dataDoc.rotation) % 180; let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; let aspect = realsize.height / realsize.width; - if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) { + if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(1 - NumCast(layoutdoc.nativeHeight) / NumCast(layoutdoc.nativeWidth) / (realsize.height / realsize.width)) > 0.1)) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; layoutdoc.nativeHeight = realsize.height; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 617b580dd..88cd2cdc4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -57,11 +57,8 @@ export class PDFBox extends DocComponent(PdfDocumen } loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; - if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) { - let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); - this.Document.nativeWidth = nw * 96 / 72; - this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72; - } + this.Document.nativeWidth = nw * 96 / 72; + this.Document.nativeHeight = nh * 96 / 72; !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); } @@ -177,12 +174,14 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } + _initialScale: number | undefined; render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; - if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true; - return (!this._everActive ? + if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale; + if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; + return (noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
{` ${this.props.Document.title}`} @@ -203,11 +202,11 @@ export class PDFBox extends DocComponent(PdfDocumen setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView} renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling} - addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} + addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} focus={this.props.focus} pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this.props.ScreenToLocalTransform().Scale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f44ca99b9..180ed9032 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -43,8 +43,8 @@ export class PresBox extends React.Component { value.forEach((item, i) => { if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = item; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = item; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); value.splice(i, 1, pinDoc); } }); @@ -124,13 +124,13 @@ export class PresBox extends React.Component { this.childDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority if (doc.hideTillShownButton && ind <= index) { - (doc.target as Doc).opacity = 1; + (doc.presentationTargetDoc as Doc).opacity = 1; } if (doc.hideAfterButton && ind < index) { - (doc.target as Doc).opacity = 0; + (doc.presentationTargetDoc as Doc).opacity = 0; } if (doc.fadeButton && ind < index) { - (doc.target as Doc).opacity = 0.5; + (doc.presentationTargetDoc as Doc).opacity = 0.5; } }); } @@ -145,13 +145,13 @@ export class PresBox extends React.Component { //the order of cases is aligned based on priority if (key.hideAfterButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.fadeButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.hideTillShownButton && ind > index) { - (key.target as Doc).opacity = 0; + (key.presentationTargetDoc as Doc).opacity = 0; } }); } @@ -162,7 +162,7 @@ export class PresBox extends React.Component { * te option open, navigates to that element. */ navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { - let fromDoc = this.childDocs[fromDocIndex].target as Doc; + let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc; let docToJump = curDoc; let willZoom = false; @@ -190,7 +190,7 @@ export class PresBox 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 - let target = await curDoc.target as Doc; + let target = await curDoc.presentationTargetDoc as Doc; if (curDoc.navButton) { DocumentManager.Instance.jumpToDocument(target, false); } else if (curDoc.showButton) { @@ -210,8 +210,8 @@ export class PresBox extends React.Component { let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(await docToJump.target as Doc, willZoom); - let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.target as Doc); + await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc); curDoc.viewScale = newScale; //saving the scale that user was on if (curScale !== 1) { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b7d9a1eab..573197117 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -129,7 +129,7 @@ export class VideoBox extends DocComponent(VideoD width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" }); this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); - DocUtils.MakeLink(imageSummary, this.props.Document); + DocUtils.MakeLink({doc:imageSummary}, {doc: this.props.Document}, "snapshot from " + this.props.Document.title, "video frame snapshot"); } }); } diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 3ed85f6a5..f7f52b3ef 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym, Opt, DocListCastAsync } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; @@ -14,6 +14,7 @@ interface IAnnotationProps { fieldExtensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Opt, where: string) => boolean; pinToPres: (document: Doc) => void; + focus: (doc: Doc) => void; } export default class Annotation extends React.Component { @@ -60,6 +61,7 @@ class RegionAnnotation extends React.Component { } componentWillUnmount() { + this._brushDisposer && this._brushDisposer(); this._reactionDisposer && this._reactionDisposer(); } @@ -94,14 +96,11 @@ class RegionAnnotation extends React.Component { PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } else if (e.button === 0) { - let targetDoc = await Cast(this.props.document.target, Doc); - if (targetDoc) { - let context = await Cast(targetDoc.targetContext, Doc); - if (context) { - DocumentManager.Instance.jumpToDocument(targetDoc, false, false, - ((doc) => this.props.addDocTab(targetDoc!, undefined, e.ctrlKey ? "onRight" : "inTab")), - undefined, undefined); - } + let annoGroup = await Cast(this.props.document.group, Doc); + if (annoGroup) { + DocumentManager.Instance.FollowLink(annoGroup, + (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"), + false, false, undefined); } } } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2202351ee..e62542014 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component { @observable public Pinned: boolean = false; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction; - public Highlight: (d: Doc | undefined, color: string) => void = emptyFunction; + public Highlight: (color: string) => void = emptyFunction; public Delete: () => void = emptyFunction; public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; @@ -156,11 +156,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this.Pinned) { - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 33226eac4..20dfc4d8c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -10,7 +10,6 @@ import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils"; -import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -44,6 +43,7 @@ interface IViewerProps { select: (isCtrlPressed: boolean) => void; startupLive: boolean; renderDepth: number; + focus: (doc: Doc) => void; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; active: () => boolean; @@ -108,7 +108,8 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1 && this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); + this.props.startupLive && this.setupPdfJsViewer(); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -136,19 +137,11 @@ export class PDFViewer extends React.Component { if (this.props.active() && e.clipboardData) { e.clipboardData.setData("text/plain", this._selectionText); e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); - e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]); + e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]); e.preventDefault(); } } - paste = (e: ClipboardEvent) => { - if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) { - let linkDocId = e.clipboardData.getData("dash/linkDoc"); - linkDocId && DocServer.GetRefField(linkDocId).then(async (link) => - (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false))); - } - } - setSelectionText = (text: string) => this._selectionText = text; @action @@ -194,13 +187,6 @@ export class PDFViewer extends React.Component { { fireImmediately: true } ); - document.removeEventListener("copy", this.copy); - document.addEventListener("copy", this.copy); - document.addEventListener("pagesinit", action(() => { - this.pdfViewer.currentScaleValue = this._zoomed = 1; - this.gotoPage(NumCast(this.props.Document.curPage, 1)); - })); - document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); this.createPdfViewer(); } @@ -212,6 +198,13 @@ export class PDFViewer extends React.Component { } return; } + document.removeEventListener("copy", this.copy); + document.addEventListener("copy", this.copy); + document.addEventListener("pagesinit", action(() => { + this.pdfViewer.currentScaleValue = this._zoomed = 1; + this.gotoPage(NumCast(this.props.Document.curPage, 1)); + })); + document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -229,19 +222,18 @@ export class PDFViewer extends React.Component { } @action - makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => { + makeAnnotationDocument = (color: string): Doc => { let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); let annoDocs: Doc[] = []; let minY = Number.MAX_VALUE; - if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) { + if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) { let anno = this._savedAnnotations.values()[0][0]; let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) }); if (anno.style.left) annoDoc.x = parseInt(anno.style.left); if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); - annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -258,7 +250,6 @@ export class PDFViewer extends React.Component { 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.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -272,9 +263,6 @@ export class PDFViewer extends React.Component { } mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); mainAnnoDocProto.annotationOn = this.props.Document; - if (sourceDoc && createLink) { - DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`); - } this._savedAnnotations.clear(); this.Index = -1; return mainAnnoDoc; @@ -529,7 +517,7 @@ export class PDFViewer extends React.Component { } if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined, "goldenrod"); + this.highlight("goldenrod"); } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -540,9 +528,9 @@ export class PDFViewer extends React.Component { } @action - highlight = (targetDoc: Doc | undefined, color: string) => { + highlight = (color: string) => { // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false); + let annotationDoc = this.makeAnnotationDocument(color); Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -555,20 +543,14 @@ export class PDFViewer extends React.Component { startDrag = (e: PointerEvent, ele: HTMLElement): void => { e.preventDefault(); e.stopPropagation(); - let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" }); - targetDoc.targetPage = this.getPageFromScroll(this._marqueeY); - let annotationDoc = this.highlight(undefined, "red"); - annotationDoc.linkedToDoc = false; + let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title }); + let annotationDoc = this.highlight("red"); let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { handlers: { - dragComplete: () => { - if (!annotationDoc.linkedToDoc) { - let annotations = DocListCast(annotationDoc.annotations); - annotations && annotations.forEach(anno => anno.target = targetDoc); - DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`); - } - } + dragComplete: () => !(dragData as any).linkedToDoc && + DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF") + }, hideSource: false }); @@ -602,6 +584,7 @@ export class PDFViewer extends React.Component { @action.bound removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; //TODO This won't create the field if it doesn't already exist let targetDataDoc = this.props.fieldExtensionDoc; let targetField = this.props.fieldExt; @@ -634,10 +617,10 @@ export class PDFViewer extends React.Component { getCoverImage = () => { if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { - setTimeout(() => { + setTimeout((() => { this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width; - }, 0); + }).bind(this), 0); } let nativeWidth = NumCast(this.props.Document.nativeWidth); let nativeHeight = NumCast(this.props.Document.nativeHeight); @@ -659,7 +642,7 @@ export class PDFViewer extends React.Component { @computed get annotationLayer() { return
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} + )}
; } @computed get pdfViewerDiv() { @@ -686,7 +669,8 @@ export class PDFViewer extends React.Component { setPreviewCursor={this.setPreviewCursor} PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} - focus={emptyFunction} + VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96} + focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} active={this.active} @@ -694,7 +678,7 @@ export class PDFViewer extends React.Component { whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} + addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} CollectionView={this.props.ContainingCollectionView} ScreenToLocalTransform={this.scrollXf} ruleProvider={undefined} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index de3242d32..daf000dc7 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -63,13 +63,11 @@ export class PresElementBox extends React.Component { this.hideTillShownButton = !this.hideTillShownButton; if (!this.hideTillShownButton) { if (this.myIndex >= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { - if (this.presentationDoc.presStatus) { - if (this.myIndex > this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex > this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -85,14 +83,12 @@ export class PresElementBox extends React.Component { this.hideAfterButton = !this.hideAfterButton; if (!this.hideAfterButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { if (this.fadeButton) this.fadeButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex < this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -108,14 +104,12 @@ export class PresElementBox extends React.Component { this.fadeButton = !this.fadeButton; if (!this.fadeButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { this.hideAfterButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0.5; - } + if (this.presentationDoc.presStatus && (this.myIndex < this.currentIndex)) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0.5; } } } @@ -162,7 +156,7 @@ export class PresElementBox extends React.Component { * presentation element. */ renderEmbeddedInline = () => { - if (!this.embedOpen || !(this.props.Document.target instanceof Doc)) { + if (!this.embedOpen || !(this.props.Document.presentationTargetDoc instanceof Doc)) { return (null); } @@ -175,8 +169,8 @@ export class PresElementBox extends React.Component { width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), }}> { let pbi = "presElementBox-interaction"; return (
{ p.focus(p.Document); e.stopPropagation(); }}> {treecontainer ? (null) : <> diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index aeffc81c4..58304cebb 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -665,10 +665,12 @@ export namespace Doc { export function BrushDoc(doc: Doc) { brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true); + return doc; } export function UnBrushDoc(doc: Doc) { brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc)); + return doc; } export class HighlightBrush { -- cgit v1.2.3-70-g09d2 From 2413d93a31ad4c97e09f79b97bc19346e72a1537 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 3 Oct 2019 16:31:31 -0400 Subject: improved search results to avoid showing aliases. improved Pdf results display. --- src/client/util/SearchUtil.ts | 34 ++++++++++++++++++++++++----- src/client/views/nodes/FormattedTextBox.tsx | 5 ----- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 17 ++++++++++----- src/client/views/pdf/PDFViewer.tsx | 33 +++++++++++++++++++--------- src/client/views/search/SearchItem.scss | 13 +++++++++-- src/client/views/search/SearchItem.tsx | 19 ++++++++++------ 7 files changed, 87 insertions(+), 36 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index e37f1f90d..6706dcb89 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -41,23 +41,45 @@ export namespace SearchUtil { } let { ids, numFound, highlighting } = result; - let lines: string[][] = ids.map(i => []); let txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), { qs: { ...options, q: query }, })); + let fileids = txtresult ? txtresult.ids : []; + let newIds: string[] = []; + let newLines: string[][] = []; await Promise.all(fileids.map(async (tr: string, i: number) => { let docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query let docResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: docQuery } })); - ids.push(...docResult.ids); - lines.push(...docResult.ids.map((dr: any) => txtresult.lines[i])); - numFound += docResult.numFound; + newIds.push(...docResult.ids); + newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i])); })); + + let theDocs: Doc[] = []; + let theLines: string[][] = []; + const textDocMap = await DocServer.GetRefFields(newIds); + const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc); + for (let i = 0; i < textDocs.length; i++) { + let testDoc = textDocs[i]; + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) { + theDocs.push(Doc.GetProto(testDoc)); + theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase()))); + } + } + const docMap = await DocServer.GetRefFields(ids); - const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc && doc.type !== DocumentType.KVP); - return { docs, numFound, highlighting, lines }; + const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc); + for (let i = 0; i < ids.length; i++) { + let testDoc = docs[i]; + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) { + theDocs.push(testDoc); + theLines.push([]); + } + } + + return { docs: theDocs, numFound: theDocs.length, highlighting, lines: theLines }; } export async function GetAliasesOfDocument(doc: Doc): Promise; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9347868b3..c37258f50 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -474,11 +474,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._searchReactionDisposer = reaction(() => { return StrCast(this.props.Document.search_string); }, searchString => { - const fieldkey = 'preview'; - let preview = false; - // if (!this._editorView && Object.keys(this.props.Document).indexOf(fieldkey) !== -1) { - // preview = true; - // } if (searchString) { this.highlightSearchTerms([searchString]); } diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 26de12a0d..134e757d1 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -122,7 +122,7 @@ class RegionAnnotation extends React.Component { left: this.props.x, width: this.props.width, height: this.props.height, - transition: "background-color 0.5s, opacity 0.5s", + transition: "opacity 0.5s", opacity: this._brushed ? 0.5 : undefined, backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.color) }} />); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 8027e93a3..a71e4f81e 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -11,10 +11,17 @@ // transform: scale(0.75); // transform-origin: top left; // } - // .textLayer { - // transform: scale(0.75); - // transform-origin: top left; - // } + .textLayer { + + mix-blend-mode: multiply; + opacity: 0.9; + } + .textLayer .highlight { + background-color: yellow; + } + .textLayer .highlight.selected { + background-color: orange; + } .page { position: relative; @@ -30,7 +37,6 @@ } .pdfViewer-overlay { - transform: scale(2.14359); transform-origin: left top; position: absolute; top: 0px; @@ -43,6 +49,7 @@ top: 0; width: 100%; pointer-events: none; + mix-blend-mode: multiply; .pdfPage-annotationBox { position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 20dfc4d8c..9ff3e1bd1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils"; +import smoothScroll, { Utils, emptyFunction, returnOne, intersectRect } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -85,6 +85,7 @@ export class PDFViewer extends React.Component { private _selectionReactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _filterReactionDisposer?: IReactionDisposer; + private _searchReactionDisposer?: IReactionDisposer; private _viewer: React.RefObject = React.createRef(); private _mainCont: React.RefObject = React.createRef(); private _selectionText: string = ""; @@ -103,12 +104,24 @@ export class PDFViewer extends React.Component { return this._annotations.filter(anno => this._script.run({ this: anno }, console.log, true).result); } + _lastSearch: string = ""; componentDidMount = async () => { // change the address to be the file address of the PNG version of each page // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); this.props.startupLive && this.setupPdfJsViewer(); + this._searchReactionDisposer = reaction(() => StrCast(this.props.Document.search_string), searchString => { + if (searchString) { + this.search(searchString, true); + this._lastSearch = searchString; + } + else { + setTimeout(() => this._lastSearch === "mxytzlaf" && this.search("mxytzlaf", true), 200); // bcz: how do we clear search highlights? + this._lastSearch && (this._lastSearch = "mxytzlaf"); + } + }, { fireImmediately: true }); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, @@ -130,6 +143,7 @@ export class PDFViewer extends React.Component { this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); this._selectionReactionDisposer && this._selectionReactionDisposer(); + this._searchReactionDisposer && this._searchReactionDisposer(); document.removeEventListener("copy", this.copy); } @@ -298,12 +312,9 @@ export class PDFViewer extends React.Component { @action scrollToAnnotation = (scrollToAnnotation: Doc) => { - this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); - let windowHgt = this.props.PanelHeight() / this.props.ContentScaling(); - let scrollRange = this._mainCont.current!.scrollHeight - windowHgt; - let pgScroll = scrollRange / this._pageSizes.length; - this._mainCont.current!.scrollTo(0, NumCast(scrollToAnnotation.y) - pgScroll / 2); - Doc.BrushDoc(scrollToAnnotation); + let offset = this.visibleHeight() / 2 * 96 / 72; + this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset); + Doc.linkFollowHighlight(scrollToAnnotation); } sendAnnotations = (page: number) => { @@ -454,7 +465,8 @@ export class PDFViewer extends React.Component { if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) { let scaleY = this._mainCont.current.offsetHeight / boundingRect.height; let scaleX = this._mainCont.current.offsetWidth / boundingRect.width; - if (rect.width !== this._mainCont.current.clientWidth) { + if (rect.width !== this._mainCont.current.clientWidth && + (i == 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) { let annoBox = document.createElement("div"); annoBox.className = "pdfPage-annotationBox"; // transforms the positions from screen onto the pdf div @@ -659,17 +671,18 @@ export class PDFViewer extends React.Component { marqueeX = () => this._marqueeX; marqueeY = () => this._marqueeY; marqueeing = () => this._marqueeing; + visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96; render() { return (
{this.pdfViewerDiv} + {this.annotationLayer}
- {this.annotationLayer} NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} - VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96} + VisibleHeight={this.visibleHeight} focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss index 273d49349..62715c5eb 100644 --- a/src/client/views/search/SearchItem.scss +++ b/src/client/views/search/SearchItem.scss @@ -4,7 +4,6 @@ display: flex; flex-direction: row-reverse; justify-content: flex-end; - height: 70px; z-index: 0; } @@ -15,9 +14,12 @@ border-color: $intermediate-color; border-bottom-style: solid; padding: 10px; - height: 70px; + min-height: 70px; + max-height: 150px; + height: auto; z-index: 0; display: inline-block; + overflow: auto; .main-search-info { display: flex; @@ -26,6 +28,7 @@ .search-title-container { width: 100%; + overflow: hidden; .search-title { text-transform: uppercase; @@ -181,6 +184,12 @@ background: $lighter-alt-accent; } +.search-highlighting { + overflow: hidden; + text-overflow: ellipsis; + white-space: pre; +} + .searchBox-instances { float: left; opacity: 1; diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 4d021216d..a7822ed46 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -4,13 +4,10 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { Doc } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { RichTextField } from "../../../new_fields/RichTextField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils"; -import { DocServer } from "../../DocServer"; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, SetupDrag } from "../../util/DragManager"; @@ -228,6 +225,12 @@ export class SearchItem extends React.Component { @action pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); } + nextHighlight = (e: React.PointerEvent) => { + e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); + let sstring = StrCast(this.props.doc.search_string); + this.props.doc.search_string = ""; + setTimeout(() => this.props.doc.search_string = sstring, 0); + } highlightDoc = (e: React.PointerEvent) => { if (this.props.doc.type === DocumentType.LINK) { if (this.props.doc.anchor1 && this.props.doc.anchor2) { @@ -240,6 +243,7 @@ export class SearchItem extends React.Component { } else { Doc.BrushDoc(this.props.doc); } + e.stopPropagation(); } unHighlightDoc = (e: React.PointerEvent) => { @@ -283,13 +287,14 @@ export class SearchItem extends React.Component { const doc2 = Cast(this.props.doc.anchor2, Doc); return (
-
+
{StrCast(this.props.doc.title)}
-
{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? "Text:" + this.props.lines[0] : ""}
+
{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}
+ {this.props.lines.filter((m, i) => i).map((l, i) =>
`${l}`
)}
-- cgit v1.2.3-70-g09d2 From 6c56481932872e9a35030cf3e44f1a6f75f88c51 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 3 Oct 2019 16:43:39 -0400 Subject: fixed docdecoration scrolling with pdfs. --- src/client/views/pdf/PDFViewer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 9ff3e1bd1..1b76ddbdc 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -331,8 +331,10 @@ export class PDFViewer extends React.Component { } } + @observable scrollTop = 0; @action onScroll = (e: React.UIEvent) => { + this.scrollTop = this._mainCont.current!.scrollTop; this.pdfViewer && (this.props.Document.curPage = this.pdfViewer.currentPageNumber); } @@ -607,7 +609,7 @@ export class PDFViewer extends React.Component { return true; } scrollXf = () => { - return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._mainCont.current.scrollTop) : this.props.ScreenToLocalTransform(); + return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.scrollTop) : this.props.ScreenToLocalTransform(); } setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { this._setPreviewCursor = func; -- cgit v1.2.3-70-g09d2 From d611773fc805082a935cae49723d516ce66e1a14 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 4 Oct 2019 23:45:01 -0400 Subject: more pdf cleanup. fix to mix-multiply-mode for better highlighters/opacity. small text box fixes. --- src/client/util/DocumentManager.ts | 2 +- src/client/util/RichTextSchema.tsx | 26 ++- src/client/util/SelectionManager.ts | 2 - src/client/views/DocumentButtonBar.tsx | 1 - src/client/views/DocumentDecorations.tsx | 1 - src/client/views/MainView.tsx | 3 +- .../collectionFreeForm/MarqueeView.scss | 2 +- src/client/views/nodes/Annotation.tsx | 117 ------------ src/client/views/nodes/FormattedTextBox.tsx | 7 +- src/client/views/pdf/Annotation.scss | 3 +- src/client/views/pdf/Annotation.tsx | 4 +- src/client/views/pdf/PDFMenu.tsx | 10 +- src/client/views/pdf/PDFViewer.scss | 6 +- src/client/views/pdf/PDFViewer.tsx | 198 +++++++++------------ src/new_fields/Doc.ts | 2 +- .../authentication/models/current_user_utils.ts | 2 +- 16 files changed, 117 insertions(+), 269 deletions(-) delete mode 100644 src/client/views/nodes/Annotation.tsx (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ffd311665..6fe97edbb 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -139,7 +139,7 @@ export class DocumentManager { const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc); finalDocView && (finalDocView.Document.scrollToLinkID = linkId); finalDocView && Doc.linkFollowHighlight(finalDocView.props.Document); - } + }; const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc); const annotatedDoc = await Cast(targetDoc.annotationOn, Doc); if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 53eaf9ce2..528c0000b 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -654,17 +654,17 @@ export class ImageResizeView { this._img.onclick = function (e: any) { e.stopPropagation(); e.preventDefault(); - if (view.state.selection.node && view.state.selection.node.type !== view.state.schema.nodes.image) - view.dispatch( - view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(view.state.selection.from - 2)))); - } + if (view.state.selection.node && view.state.selection.node.type !== view.state.schema.nodes.image) { + view.dispatch(view.state.tr.setSelection(new NodeSelection(view.state.doc.resolve(view.state.selection.from - 2)))); + } + }; this._img.onpointerdown = function (e: any) { if (e.ctrlKey) { e.preventDefault(); e.stopPropagation(); DocServer.GetRefField(node.attrs.docid).then(async linkDoc => (linkDoc instanceof Doc) && - DocumentManager.Instance.FollowLink(linkDoc, (view.state.schema as any).Document, + DocumentManager.Instance.FollowLink(linkDoc, view.state.schema.Document, document => addDocTab(document, undefined, node.attrs.location ? node.attrs.location : "inTab"), false)); } }; @@ -730,7 +730,7 @@ export class DashDocView { this._dashSpan.style.width = node.attrs.width; this._dashSpan.style.height = node.attrs.height; this._dashSpan.style.position = "absolute"; - this._dashSpan.style.display = "inline-block" + this._dashSpan.style.display = "inline-block"; this._handle.style.position = "absolute"; this._handle.style.width = "20px"; this._handle.style.height = "20px"; @@ -771,16 +771,10 @@ export class DashDocView { this._dashSpan.onclick = function (e: any) { FormattedTextBox.firstTarget && FormattedTextBox.firstTarget(); e.stopPropagation(); - } - this._dashSpan.onkeydown = function (e: any) { - e.stopPropagation(); - } - this._dashSpan.onkeypress = function (e: any) { - e.stopPropagation(); - } - this._dashSpan.onkeyup = function (e: any) { - e.stopPropagation(); - } + }; + this._dashSpan.onkeydown = function (e: any) { e.stopPropagation(); }; + this._dashSpan.onkeypress = function (e: any) { e.stopPropagation(); }; + this._dashSpan.onkeyup = function (e: any) { e.stopPropagation(); }; this._handle.onpointerdown = function (e: any) { e.preventDefault(); e.stopPropagation(); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index a02a270ee..df1b46b33 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -27,7 +27,6 @@ export namespace SelectionManager { } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) { manager.SelectedDocuments.map(dv => dv !== docView && dv.props.whenActiveChanged(false)); manager.SelectedDocuments = [docView]; - FormattedTextBox.InputBoxOverlay = undefined; } } @action @@ -42,7 +41,6 @@ export namespace SelectionManager { DeselectAll(): void { manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments = []; - FormattedTextBox.InputBoxOverlay = undefined; } } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index e57745b86..9e2d41621 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -140,7 +140,6 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[], let selDoc = this.props.views[0]; let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined; let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []); - FormattedTextBox.InputBoxOverlay = undefined; this._linkDrag = UndoManager.StartBatch("Drag Link"); DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, { handlers: { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 26ffaf3a6..9acb77ce2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -466,7 +466,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } - if (!this._resizing) runInAction(() => FormattedTextBox.InputBoxOverlay = undefined); SelectionManager.SelectedDocuments().forEach(element => { if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 545f99a41..3b0457dff 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,6 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons'; -import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss index 7dc54ea79..04f6ec2ad 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss @@ -11,7 +11,7 @@ } .marqueeView:focus-within { - overflow: visible; + overflow: hidden; } .marquee { border-style: dashed; diff --git a/src/client/views/nodes/Annotation.tsx b/src/client/views/nodes/Annotation.tsx deleted file mode 100644 index 3e4ed6bf1..000000000 --- a/src/client/views/nodes/Annotation.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import "./ImageBox.scss"; -import React = require("react"); -import { observer } from "mobx-react"; -import { observable, action } from 'mobx'; -import 'react-pdf/dist/Page/AnnotationLayer.css'; - -interface IProps { - Span: HTMLSpanElement; - X: number; - Y: number; - Highlights: any[]; - Annotations: any[]; - CurrAnno: any[]; - -} - -/** - * Annotation class is used to take notes on a particular highlight. You can also change highlighted span's color - * Improvements to be made: Removing the annotation when onRemove is called. (Removing this, not just the highlighted span). - * Also need to support multiline highlighting - * - * Written by: Andrew Kim - */ -@observer -export class Annotation extends React.Component { - - /** - * changes color of the span (highlighted section) - */ - onColorChange = (e: React.PointerEvent) => { - if (e.currentTarget.innerHTML === "r") { - this.props.Span.style.backgroundColor = "rgba(255,0,0, 0.3)"; - } else if (e.currentTarget.innerHTML === "b") { - this.props.Span.style.backgroundColor = "rgba(0,255, 255, 0.3)"; - } else if (e.currentTarget.innerHTML === "y") { - this.props.Span.style.backgroundColor = "rgba(255,255,0, 0.3)"; - } else if (e.currentTarget.innerHTML === "g") { - this.props.Span.style.backgroundColor = "rgba(76, 175, 80, 0.3)"; - } - - } - - /** - * removes the highlighted span. Supposed to remove Annotation too, but I don't know how to unmount this - */ - @action - onRemove = (e: any) => { - let index: number = -1; - //finding the highlight in the highlight array - this.props.Highlights.forEach((e) => { - for (const span of e.spans) { - if (span === this.props.Span) { - index = this.props.Highlights.indexOf(e); - this.props.Highlights.splice(index, 1); - } - } - }); - - //removing from CurrAnno and Annotation array - this.props.Annotations.splice(index, 1); - this.props.CurrAnno.pop(); - - //removing span from div - if (this.props.Span.parentElement) { - let nodesArray = this.props.Span.parentElement.childNodes; - nodesArray.forEach((e) => { - if (e === this.props.Span) { - if (this.props.Span.parentElement) { - this.props.Highlights.forEach((item) => { - if (item === e) { - item.remove(); - } - }); - e.remove(); - } - } - }); - } - - - } - - render() { - return ( -
-
- -
- - - - -
- -
-
- -
-
- - ); - } -} \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 05904e1e7..2b6a86aed 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -291,7 +291,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (de.data.urlField && link) { let url: string = de.data.urlField.url.href; let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image; - node = model.create({ src: url, docid: link[Id] }) + node = model.create({ src: url, docid: link[Id] }); } else { node = schema.nodes.dashDoc.create({ width: target[WidthSym](), height: target[HeightSym](), @@ -798,7 +798,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._editorView!.focus(); } } - } + }; } onPointerUp = (e: React.PointerEvent): void => { @@ -807,9 +807,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe e.stopPropagation(); } } - + static InputBoxOverlay: any = null; @action onFocused = (e: React.FocusEvent): void => { + FormattedTextBox.InputBoxOverlay = this; document.removeEventListener("keypress", this.recordKeyHandler); document.addEventListener("keypress", this.recordKeyHandler); this.tryUpdateHeight(); diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss index 0c6df74f0..cc326eb93 100644 --- a/src/client/views/pdf/Annotation.scss +++ b/src/client/views/pdf/Annotation.scss @@ -2,6 +2,5 @@ pointer-events: all; user-select: none; position: absolute; - background-color: red; - opacity: 0.1; + background-color: rgba(146, 245, 95, 0.467); } \ No newline at end of file diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 134e757d1..4bb166ffe 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -122,9 +122,9 @@ class RegionAnnotation extends React.Component { left: this.props.x, width: this.props.width, height: this.props.height, - transition: "opacity 0.5s", opacity: this._brushed ? 0.5 : undefined, - backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.color) + backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor), + transition: "opacity 0.5s", }} />); } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index e62542014..1e3320069 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -4,7 +4,7 @@ import { observable, action, } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { emptyFunction, returnFalse } from "../../../Utils"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, Opt } from "../../../new_fields/Doc"; @observer export default class PDFMenu extends React.Component { @@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component { @observable public Pinned: boolean = false; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction; - public Highlight: (color: string) => void = emptyFunction; + public Highlight: (color: string) => Opt = (color: string) => undefined; public Delete: () => void = emptyFunction; public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; @@ -155,12 +155,8 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { - if (!this.Pinned) { - this.Highlight("#f4f442"); - } - else { + if (!this.Highlight("rgba(245, 230, 95, 0.616)") && this.Pinned) { this.Highlighting = !this.Highlighting; - this.Highlight("#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a71e4f81e..c77cee792 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -16,6 +16,7 @@ mix-blend-mode: multiply; opacity: 0.9; } + .textLayer ::selection { background: yellow; } // should match the backgroundColor in createAnnotation() .textLayer .highlight { background-color: yellow; } @@ -51,10 +52,9 @@ pointer-events: none; mix-blend-mode: multiply; - .pdfPage-annotationBox { + .pdfViewer-annotationBox { position: absolute; - background-color: red; - opacity: 0.1; + background-color: rgba(245, 230, 95, 0.616); } } .pdfViewer-waiting { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1b76ddbdc..4516e9904 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -75,8 +75,9 @@ export class PDFViewer extends React.Component { @observable private _showWaiting = true; @observable private _showCover = false; @observable private _zoomed = 1; + @observable private _scrollTop = 0; - public pdfViewer: any; + private _pdfViewer: any; private _retries = 0; // number of times tried to create the PDF viewer private _isChildActive = false; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @@ -149,15 +150,16 @@ export class PDFViewer extends React.Component { copy = (e: ClipboardEvent) => { if (this.props.active() && e.clipboardData) { - e.clipboardData.setData("text/plain", this._selectionText); - e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); - e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]); + let annoDoc = this.makeAnnotationDocument("#0390fc"); + if (annoDoc) { + e.clipboardData.setData("text/plain", this._selectionText); + e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); + e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]); + } e.preventDefault(); } } - setSelectionText = (text: string) => this._selectionText = text; - @action initialLoad = async () => { if (this._pageSizes.length === 0) { @@ -215,7 +217,7 @@ export class PDFViewer extends React.Component { document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); document.addEventListener("pagesinit", action(() => { - this.pdfViewer.currentScaleValue = this._zoomed = 1; + this._pdfViewer.currentScaleValue = this._zoomed = 1; this.gotoPage(NumCast(this.props.Document.curPage, 1)); })); document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); @@ -223,36 +225,35 @@ export class PDFViewer extends React.Component { let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, }); - this.pdfViewer = new PDFJSViewer.PDFViewer({ + this._pdfViewer = new PDFJSViewer.PDFViewer({ container: this._mainCont.current, viewer: this._viewer.current, linkService: pdfLinkService, findController: pdfFindController, renderer: "canvas", }); - pdfLinkService.setViewer(this.pdfViewer); + pdfLinkService.setViewer(this._pdfViewer); pdfLinkService.setDocument(this.props.pdf, null); - this.pdfViewer.setDocument(this.props.pdf); + this._pdfViewer.setDocument(this.props.pdf); } @action - makeAnnotationDocument = (color: string): Doc => { + makeAnnotationDocument = (color: string): Opt => { + if (this._savedAnnotations.size() === 0) return undefined; let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); let annoDocs: Doc[] = []; let minY = Number.MAX_VALUE; - if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) { + if ((this._savedAnnotations.values()[0][0] as any).marqueeing) { let anno = this._savedAnnotations.values()[0][0]; - let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) }); + let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, title: "Annotation on " + StrCast(this.props.Document.title) }); if (anno.style.left) annoDoc.x = parseInt(anno.style.left); if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); annoDoc.group = mainAnnoDoc; - annoDoc.color = color; - annoDoc.type = AnnotationTypes.Region; - annoDocs.push(annoDoc); annoDoc.isButton = true; + annoDocs.push(annoDoc); anno.remove(); mainAnnoDoc = annoDoc; mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); @@ -265,8 +266,7 @@ export class PDFViewer extends React.Component { if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); annoDoc.group = mainAnnoDoc; - annoDoc.color = color; - annoDoc.type = AnnotationTypes.Region; + annoDoc.backgroundColor = color; annoDocs.push(annoDoc); anno.remove(); (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY)); @@ -307,7 +307,7 @@ export class PDFViewer extends React.Component { @action gotoPage = (p: number) => { - this.pdfViewer && this.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); + this._pdfViewer && this._pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); } @action @@ -317,25 +317,11 @@ export class PDFViewer extends React.Component { Doc.linkFollowHighlight(scrollToAnnotation); } - sendAnnotations = (page: number) => { - return this._savedAnnotations.getValue(page); - } - - receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { - 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); - } - } - @observable scrollTop = 0; @action onScroll = (e: React.UIEvent) => { - this.scrollTop = this._mainCont.current!.scrollTop; - this.pdfViewer && (this.props.Document.curPage = this.pdfViewer.currentPageNumber); + this._scrollTop = this._mainCont.current!.scrollTop; + this._pdfViewer && (this.props.Document.curPage = this._pdfViewer.currentPageNumber); } // get the page index that the vertical offset passed in is on @@ -355,6 +341,8 @@ export class PDFViewer extends React.Component { div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString(); } this._annotationLayer.current.append(div); + div.style.backgroundColor = "yellow"; + div.style.opacity = "0.5"; let savedPage = this._savedAnnotations.getValue(page); if (savedPage) { savedPage.push(div); @@ -371,8 +359,8 @@ export class PDFViewer extends React.Component { if (!searchString) { fwd ? this.nextAnnotation() : this.prevAnnotation(); } - else if (this.pdfViewer._pageViewsReady) { - this.pdfViewer.findController.executeCommand('findagain', { + else if (this._pdfViewer._pageViewsReady) { + this._pdfViewer.findController.executeCommand('findagain', { caseSensitive: false, findPrevious: !fwd, highlightAll: true, @@ -382,7 +370,7 @@ export class PDFViewer extends React.Component { } else if (this._mainCont.current) { let executeFind = () => { - this.pdfViewer.findController.executeCommand('find', { + this._pdfViewer.findController.executeCommand('find', { caseSensitive: false, findPrevious: !fwd, highlightAll: true, @@ -406,30 +394,24 @@ export class PDFViewer extends React.Component { } this._marqueeing = false; if (!e.altKey && e.button === 0 && this.active()) { + // clear out old marquees and initialize menu for new selection PDFMenu.Instance.StartDrag = this.startDrag; PDFMenu.Instance.Highlight = this.highlight; PDFMenu.Instance.Snippet = this.createSnippet; PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); + this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, [])); if (e.target && (e.target as any).parentElement.className === "textLayer") { - if (!e.ctrlKey) { - this.receiveAnnotations([], -1); - } + // start selecting text if mouse down on textLayer spans } - else { + else if (this._mainCont.current) { // set marquee x and y positions to the spatially transformed position - if (this._mainCont.current) { - let boundingRect = this._mainCont.current.getBoundingClientRect(); - this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width); - this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop; - } + let boundingRect = this._mainCont.current.getBoundingClientRect(); + this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width); + this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop; + this._marqueeHeight = this._marqueeWidth = 0; this._marqueeing = true; - let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox"); - if (marquees && marquees.length) { // make a copy of the marquee - let marquee = marquees[0] as HTMLDivElement; - marquee.style.opacity = "0.2"; - } - this.receiveAnnotations([], -1); } document.removeEventListener("pointermove", this.onSelectMove); document.addEventListener("pointermove", this.onSelectMove); @@ -464,13 +446,13 @@ export class PDFViewer extends React.Component { let clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); - if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) { + if (rect) { let scaleY = this._mainCont.current.offsetHeight / boundingRect.height; let scaleX = this._mainCont.current.offsetWidth / boundingRect.width; if (rect.width !== this._mainCont.current.clientWidth && - (i == 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) { + (i === 0 || !intersectRect(clientRects[i], clientRects[i - 1]))) { let annoBox = document.createElement("div"); - annoBox.className = "pdfPage-annotationBox"; + annoBox.className = "pdfViewer-annotationBox"; // transforms the positions from screen onto the pdf div annoBox.style.top = ((rect.top - boundingRect.top) * scaleY + this._mainCont.current.scrollTop).toString(); annoBox.style.left = ((rect.left - boundingRect.left) * scaleX).toString(); @@ -481,8 +463,7 @@ export class PDFViewer extends React.Component { } } } - let text = selRange.cloneContents().textContent; - text && this.setSelectionText(text); + this._selectionText = selRange.cloneContents().textContent || ""; // clear selection if (sel.empty) { // Chrome @@ -494,22 +475,22 @@ export class PDFViewer extends React.Component { @action onSelectEnd = (e: PointerEvent): void => { + this._savedAnnotations.clear(); if (this._marqueeing) { if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox"); - if (marquees && marquees.length) { // make a copy of the marquee + if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation. + let style = (marquees[0] as HTMLDivElement).style; let copy = document.createElement("div"); - let marquee = marquees[0] as HTMLDivElement; - let style = marquee.style; copy.style.left = style.left; copy.style.top = style.top; copy.style.width = style.width; copy.style.height = style.height; copy.style.border = style.border; copy.style.opacity = style.opacity; - copy.className = "pdfPage-annotationBox"; + (copy as any).marqueeing = true; + copy.className = "pdfViewer-annotationBox"; this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY)); - marquee.style.opacity = "0"; } if (!e.ctrlKey) { @@ -518,8 +499,7 @@ export class PDFViewer extends React.Component { } PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } - - this._marqueeHeight = this._marqueeWidth = 0; + this._marqueeing = false; } else { let sel = window.getSelection(); @@ -531,7 +511,7 @@ export class PDFViewer extends React.Component { } if (PDFMenu.Instance.Highlighting) { - this.highlight("goldenrod"); + this.highlight("rgba(245, 230, 95, 0.616)"); // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -545,7 +525,7 @@ export class PDFViewer extends React.Component { highlight = (color: string) => { // creates annotation documents for current highlights let annotationDoc = this.makeAnnotationDocument(color); - Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); + annotationDoc && Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -558,16 +538,18 @@ export class PDFViewer extends React.Component { e.preventDefault(); e.stopPropagation(); let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title }); - let annotationDoc = this.highlight("red"); - let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); - DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { - handlers: { - dragComplete: () => !(dragData as any).linkedToDoc && - DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF") - - }, - hideSource: false - }); + const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); + if (annotationDoc) { + let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); + DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: () => !(dragData as any).linkedToDoc && + DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF") + + }, + hideSource: false + }); + } } createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => { @@ -609,7 +591,7 @@ export class PDFViewer extends React.Component { return true; } scrollXf = () => { - return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.scrollTop) : this.props.ScreenToLocalTransform(); + return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform(); } setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { this._setPreviewCursor = func; @@ -647,9 +629,9 @@ export class PDFViewer extends React.Component { onZoomWheel = (e: React.WheelEvent) => { e.stopPropagation(); if (e.ctrlKey) { - let curScale = Number(this.pdfViewer.currentScaleValue); - this.pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale + curScale * e.deltaY / 1000)); - this._zoomed = Number(this.pdfViewer.currentScaleValue); + let curScale = Number(this._pdfViewer.currentScaleValue); + this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale + curScale * e.deltaY / 1000)); + this._zoomed = Number(this._pdfViewer.currentScaleValue); } } @@ -657,29 +639,7 @@ export class PDFViewer extends React.Component { return
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )} -
; - } - @computed get pdfViewerDiv() { - return
; - } - @computed get standinViews() { - return <> - {this._showCover ? this.getCoverImage() : (null)} - {this._showWaiting ? : (null)} - ; - } - marqueeWidth = () => this._marqueeWidth; - marqueeHeight = () => this._marqueeHeight; - marqueeX = () => this._marqueeX; - marqueeY = () => this._marqueeY; - marqueeing = () => this._marqueeing; - visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96; - render() { - return (
- {this.pdfViewerDiv} - - {this.annotationLayer} -
+
NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} @@ -702,7 +662,29 @@ export class PDFViewer extends React.Component { chromeCollapsed={true}>
+
; + } + @computed get pdfViewerDiv() { + return
; + } + @computed get standinViews() { + return <> + {this._showCover ? this.getCoverImage() : (null)} + {this._showWaiting ? : (null)} + ; + } + marqueeWidth = () => this._marqueeWidth; + marqueeHeight = () => this._marqueeHeight; + marqueeX = () => this._marqueeX; + marqueeY = () => this._marqueeY; + marqueeing = () => this._marqueeing; + visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96; + render() { + return (
+ {this.pdfViewerDiv} + {this.annotationLayer} {this.standinViews} +
); } } @@ -722,11 +704,9 @@ class PdfViewerMarquee extends React.Component { style={{ left: `${this.props.x()}px`, top: `${this.props.y()}px`, width: `${this.props.width()}px`, height: `${this.props.height()}px`, - border: `${this.props.width() === 0 ? "" : "2px dashed black"}` + border: `${this.props.width() === 0 ? "" : "2px dashed black"}`, + opacity: 0.2 }}>
; } -} - - -export enum AnnotationTypes { Region } +} \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 6acc6e1ca..7e37eba84 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -685,7 +685,7 @@ export namespace Doc { document.removeEventListener("pointerdown", linkFollowUnhighlight); document.addEventListener("pointerdown", linkFollowUnhighlight); let x = dt = Date.now(); - window.setTimeout(() => dt == x && linkFollowUnhighlight(), 5000); + window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000); } export class HighlightBrush { diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b2509a4f1..0fbfbf2f3 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -112,7 +112,7 @@ export class CurrentUserUtils { if (sidebar) { sidebar.backgroundColor = "lightgrey"; } - }) + }); if (doc.overlays === undefined) { const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" }); -- cgit v1.2.3-70-g09d2 From 29cdf0d66df3e38408f69ac8225b4d59397ee2e6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 8 Oct 2019 19:02:35 -0400 Subject: fixes for text selection in pdfs to be less jumpy and to work through other annotations. --- src/Utils.ts | 2 +- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 4 ++++ src/client/views/pdf/PDFViewer.tsx | 15 ++++++++++----- 6 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 489de3b50..9a2f01f80 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -320,7 +320,7 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start; }; -export default function smoothScroll(duration: number, element: HTMLElement, to: number) { +export function smoothScroll(duration: number, element: HTMLElement, to: number) { const start = element.scrollTop; const change = to - start; const startDate = new Date().getTime(); diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 5f2bc18b5..fe4c5ac9f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -545,7 +545,7 @@ export class TooltipTextMenu { view.dispatch(tx2); })) { let tx2 = view.state.tr; - let tx3 = updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle); + let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2; marks && tx3.ensureMarks([...marks]); marks && tx3.setStoredMarks([...marks]); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 86166b0b3..67d1bc42c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -548,7 +548,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let editor = this._editorView; let ret = findLinkFrag(editor.state.doc.content, editor); - if (ret.frag.size > 2) { + if (ret.frag.size > 2 && ret.start >= 0) { let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 1e3320069..517a99a68 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -155,7 +155,7 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { - if (!this.Highlight("rgba(245, 230, 95, 0.616)") && this.Pinned) { + if (!this.Highlight("rgba(245, 230, 95, 0.616)") && this.Pinned) { // yellowish highlight color for a marker type highlight this.Highlighting = !this.Highlighting; } } diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index c77cee792..f6fedf3da 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -15,6 +15,10 @@ mix-blend-mode: multiply; opacity: 0.9; + span { + padding-right: 5px; + padding-bottom: 4px; + } } .textLayer ::selection { background: yellow; } // should match the backgroundColor in createAnnotation() .textLayer .highlight { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4516e9904..b010d16c8 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import smoothScroll, { Utils, emptyFunction, returnOne, intersectRect } from "../../../Utils"; +import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -24,6 +24,7 @@ import { CollectionView } from "../collections/CollectionView"; import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { SelectionManager } from "../../util/SelectionManager"; +import { undoBatch } from "../../util/UndoManager"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -62,6 +63,7 @@ interface IViewerProps { */ @observer export class PDFViewer extends React.Component { + static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @observable private _savedAnnotations: Dictionary = new Dictionary(); @@ -150,7 +152,7 @@ export class PDFViewer extends React.Component { copy = (e: ClipboardEvent) => { if (this.props.active() && e.clipboardData) { - let annoDoc = this.makeAnnotationDocument("#0390fc"); + let annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish) if (annoDoc) { e.clipboardData.setData("text/plain", this._selectionText); e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); @@ -237,6 +239,7 @@ export class PDFViewer extends React.Component { this._pdfViewer.setDocument(this.props.pdf); } + @undoBatch @action makeAnnotationDocument = (color: string): Opt => { if (this._savedAnnotations.size() === 0) return undefined; @@ -388,6 +391,7 @@ export class PDFViewer extends React.Component { // if alt+left click, drag and annotate this._downX = e.clientX; this._downY = e.clientY; + addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" }); if (NumCast(this.props.Document.scale, 1) !== 1) return; if ((e.button !== 0 || e.altKey) && this.active()) { this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); @@ -475,6 +479,7 @@ export class PDFViewer extends React.Component { @action onSelectEnd = (e: PointerEvent): void => { + clearStyleSheetRules(PDFViewer._annotationStyle); this._savedAnnotations.clear(); if (this._marqueeing) { if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { @@ -510,8 +515,8 @@ export class PDFViewer extends React.Component { } } - if (PDFMenu.Instance.Highlighting) { - this.highlight("rgba(245, 230, 95, 0.616)"); // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up + if (PDFMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up + this.highlight("rgba(245, 230, 95, 0.616)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color) } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -538,7 +543,7 @@ export class PDFViewer extends React.Component { e.preventDefault(); e.stopPropagation(); let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title }); - const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); + const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection if (annotationDoc) { let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { -- cgit v1.2.3-70-g09d2 From 2cec74403daf057d6e2e830a0544c1254722dcde Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Oct 2019 10:04:46 -0400 Subject: fixed scrolltoannotation bug in pdfviewer. removed CollectionPdfView --- package.json | 2 +- src/client/util/DocumentManager.ts | 5 ++- src/client/util/SharingManager.tsx | 3 +- .../views/collections/CollectionPDFView.scss | 11 ------- src/client/views/collections/CollectionPDFView.tsx | 37 ---------------------- .../views/collections/CollectionSchemaCells.tsx | 5 ++- .../views/collections/CollectionSchemaView.tsx | 7 ++-- src/client/views/collections/CollectionSubView.tsx | 3 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/FieldView.tsx | 3 +- src/client/views/pdf/PDFViewer.tsx | 11 ++++--- 12 files changed, 19 insertions(+), 74 deletions(-) delete mode 100644 src/client/views/collections/CollectionPDFView.scss delete mode 100644 src/client/views/collections/CollectionPDFView.tsx (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/package.json b/package.json index 6e96f94d2..8cbbb84af 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "nodemailer": "^5.1.1", "nodemon": "^1.18.10", "normalize.css": "^8.0.1", - "npm": "^6.11.3", + "npm": "^6.12.0", "p-limit": "^2.2.0", "passport": "^0.4.0", "passport-google-oauth20": "^2.0.0", diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c45d3a75f..c95d923cb 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -3,7 +3,6 @@ import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { CollectionView } from '../views/collections/CollectionView'; import { DocumentView } from '../views/nodes/DocumentView'; @@ -57,7 +56,7 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { + public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; @@ -82,7 +81,7 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 1541cd6b2..c989b6c17 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,7 +18,6 @@ import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; import { CollectionVideoView } from "../views/collections/CollectionVideoView"; -import { CollectionPDFView } from "../views/collections/CollectionPDFView"; import { CollectionView } from "../views/collections/CollectionView"; library.add(fa.faCopy); @@ -186,7 +185,7 @@ export default class SharingManager extends React.Component<{}> { className={"focus-span"} title={title} onClick={() => { - let context: Opt; + let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss deleted file mode 100644 index 62ec8a5be..000000000 --- a/src/client/views/collections/CollectionPDFView.scss +++ /dev/null @@ -1,11 +0,0 @@ - - -.collectionPdfView-cont { - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - z-index: -1; - overflow: hidden !important; -} diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx deleted file mode 100644 index cc8142ec0..000000000 --- a/src/client/views/collections/CollectionPDFView.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { trace } from "mobx"; -import { observer } from "mobx-react"; -import { Id } from "../../../new_fields/FieldSymbols"; -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"); - - -@observer -export class CollectionPDFView extends React.Component { - public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") { - return FieldView.LayoutString(CollectionPDFView, fieldKey, fieldExt); - } - - 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 - ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction, icon: "file-pdf" }); - } - } - - subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - return (); - } - - render() { - trace(); - return ( - - {this.subView} - - ); - } -} \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 179e44266..fd1362848 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -13,7 +13,6 @@ import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../globalCssVariables.s import '../DocumentDecorations.scss'; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { CollectionPDFView } from "./CollectionPDFView"; import "./CollectionSchemaView.scss"; import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; @@ -34,8 +33,8 @@ export interface CellProps { row: number; col: number; rowProps: CellInfo; - CollectionView: Opt; - ContainingCollection: Opt; + CollectionView: Opt; + ContainingCollection: Opt; Document: Doc; fieldKey: string; renderDepth: number; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 1ba35a52b..670c6bd43 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -21,7 +21,6 @@ import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss'; import { ContextMenu } from "../ContextMenu"; import '../DocumentDecorations.scss'; import { DocumentView } from "../nodes/DocumentView"; -import { CollectionPDFView } from "./CollectionPDFView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionVideoView } from "./CollectionVideoView"; @@ -247,8 +246,8 @@ export interface SchemaTableProps { PanelHeight: () => number; PanelWidth: () => number; childDocs?: Doc[]; - CollectionView: Opt; - ContainingCollectionView: Opt; + CollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; fieldKey: string; renderDepth: number; @@ -905,7 +904,7 @@ interface CollectionSchemaPreviewProps { ruleProvider: Doc | undefined; focus?: (doc: Doc) => void; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; - CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView?: CollectionView | CollectionVideoView; CollectionDoc?: Doc; onClick?: ScriptField; getTransform: () => Transform; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 10937fba2..5e2b79278 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -18,7 +18,6 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox"; -import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import React = require("react"); @@ -38,7 +37,7 @@ export interface CollectionViewProps extends FieldViewProps { } export interface SubCollectionViewProps extends CollectionViewProps { - CollectionView: Opt; + CollectionView: Opt; ruleProvider: Doc | undefined; } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 75dd27f46..a824ae9cc 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -8,7 +8,6 @@ import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; -import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; @@ -101,7 +100,7 @@ export class DocumentContentsView extends React.Component; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; Document: Doc; DataDoc?: Doc; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index b93c78cfd..c17730f48 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -8,7 +8,6 @@ import { List } from "../../../new_fields/List"; import { RichTextField } from "../../../new_fields/RichTextField"; import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField"; import { Transform } from "../../util/Transform"; -import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { AudioBox } from "./AudioBox"; @@ -29,7 +28,7 @@ export interface FieldViewProps { fieldExt: string; leaveNativeSize?: boolean; fitToBox?: boolean; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; ruleProvider: Doc | undefined; Document: Doc; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b010d16c8..65ca830d9 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -18,7 +18,6 @@ import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); import * as rp from "request-promise"; -import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import Annotation from "./Annotation"; @@ -54,7 +53,7 @@ interface IViewerProps { addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; whenActiveChanged: (isActive: boolean) => void; } @@ -315,9 +314,11 @@ export class PDFViewer extends React.Component { @action scrollToAnnotation = (scrollToAnnotation: Doc) => { - let offset = this.visibleHeight() / 2 * 96 / 72; - this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset); - Doc.linkFollowHighlight(scrollToAnnotation); + if (scrollToAnnotation) { + let offset = this.visibleHeight() / 2 * 96 / 72; + this._mainCont.current && smoothScroll(500, this._mainCont.current, NumCast(scrollToAnnotation.y) - offset); + Doc.linkFollowHighlight(scrollToAnnotation); + } } -- cgit v1.2.3-70-g09d2 From 77d66d159d75442ff5635c4bf4843b6155883cc2 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Oct 2019 14:04:22 -0400 Subject: removed CollectionVideoView --- src/client/documents/Documents.ts | 3 +- src/client/util/DocumentManager.ts | 5 +- src/client/util/SharingManager.tsx | 3 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/PreviewCursor.tsx | 18 +-- .../views/collections/CollectionBaseView.tsx | 15 +- .../views/collections/CollectionSchemaCells.tsx | 7 +- .../views/collections/CollectionSchemaView.tsx | 9 +- src/client/views/collections/CollectionSubView.tsx | 17 +- .../views/collections/CollectionVideoView.scss | 51 ------ .../views/collections/CollectionVideoView.tsx | 115 ------------- .../CollectionFreeFormLinkView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +- .../collections/collectionFreeForm/MarqueeView.tsx | 14 +- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 7 +- src/client/views/nodes/FieldView.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.scss | 57 ++++++- src/client/views/nodes/VideoBox.tsx | 177 +++++++++++++++++++-- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 18 ++- 23 files changed, 284 insertions(+), 258 deletions(-) delete mode 100644 src/client/views/collections/CollectionVideoView.scss delete mode 100644 src/client/views/collections/CollectionVideoView.tsx (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9d1a6ed3e..22aa74634 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,6 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField"; import { HistogramBox } from "../northstar/dash-nodes/HistogramBox"; import { HistogramOperation } from "../northstar/operations/HistogramOperation"; -import { CollectionVideoView } from "../views/collections/CollectionVideoView"; import { CollectionView } from "../views/collections/CollectionView"; import { CollectionViewType } from "../views/collections/CollectionBaseView"; import { AudioBox } from "../views/nodes/AudioBox"; @@ -137,7 +136,7 @@ export namespace Docs { options: { height: 150 } }], [DocumentType.VID, { - layout: { view: VideoBox, collectionView: [CollectionVideoView, data, anno] as CollectionViewType }, + layout: { view: VideoBox, ext: anno }, options: { currentTimecode: 0 }, }], [DocumentType.AUDIO, { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c95d923cb..24285a70a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -3,7 +3,6 @@ import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { CollectionView } from '../views/collections/CollectionView'; import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; @@ -56,7 +55,7 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { + public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined { let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; @@ -81,7 +80,7 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index c989b6c17..d37cd1b80 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -17,7 +17,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; -import { CollectionVideoView } from "../views/collections/CollectionVideoView"; import { CollectionView } from "../views/collections/CollectionView"; library.add(fa.faCopy); @@ -185,7 +184,7 @@ export default class SharingManager extends React.Component<{}> { className={"focus-span"} title={title} onClick={() => { - let context: Opt; + let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4f9bdbe9c..1d9f0c74b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -333,7 +333,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> iconDoc.y = NumCast(doc.y) - 24; iconDoc.maximizedDocs = new List(selected.map(s => s.props.Document)); selected.length === 1 && (doc.minimizedDoc = iconDoc); - selected[0].props.addDocument && selected[0].props.addDocument(iconDoc, false); + selected[0].props.addDocument && selected[0].props.addDocument(iconDoc); return iconDoc; } @action diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 1aed51e64..eed2cc5da 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -14,7 +14,7 @@ export class PreviewCursor extends React.Component<{}> { static _onKeyPress?: (e: KeyboardEvent) => void; static _getTransform: () => Transform; static _addLiveTextDoc: (doc: Doc) => void; - static _addDocument: (doc: Doc, allowDuplicates: false) => boolean; + static _addDocument: (doc: Doc) => boolean; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; //when focus is lost, this will remove the preview cursor @@ -44,7 +44,7 @@ export class PreviewCursor extends React.Component<{}> { title: url, width: 400, height: 315, nativeWidth: 600, nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] - }), false); + })); return; } @@ -56,7 +56,7 @@ export class PreviewCursor extends React.Component<{}> { title: url, width: 300, height: 300, // nativeWidth: 300, nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] - }), false); + })); return; } @@ -79,11 +79,11 @@ export class PreviewCursor extends React.Component<{}> { let img: Doc = Docs.Create.ImageDocument( arr[1], { - width: 300, title: arr[1], - x: newPoint[0], - y: newPoint[1], - }); - PreviewCursor._addDocument(img, false); + width: 300, title: arr[1], + x: newPoint[0], + y: newPoint[1], + }); + PreviewCursor._addDocument(img); return; } @@ -113,7 +113,7 @@ export class PreviewCursor extends React.Component<{}> { onKeyPress: (e: KeyboardEvent) => void, addLiveText: (doc: Doc) => void, getTransform: () => Transform, - addDocument: (doc: Doc, allowDuplicates: false) => boolean) { + addDocument: (doc: Doc) => boolean) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; this._addLiveTextDoc = addLiveText; diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 62be1fc31..61919427a 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -45,7 +45,7 @@ export namespace CollectionViewType { } export interface CollectionRenderProps { - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; active: () => boolean; @@ -100,22 +100,13 @@ export class CollectionBaseView extends React.Component { @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } @action.bound - addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { - var curTime = NumCast(this.props.Document.currentTimecode, -1); - curTime !== -1 && (doc.displayTimecode = curTime); + addDocument(doc: Doc): boolean { if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer Doc.GetProto(doc).annotationOn = this.props.Document; } let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; - const value = Cast(targetDataDoc[targetField], listSpec(Doc)); - if (value !== undefined) { - if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { - value.push(doc); - } - } else { - Doc.GetProto(targetDataDoc)[targetField] = new List([doc]); - } + Doc.AddDocToList(targetDataDoc, targetField, doc); Doc.GetProto(doc).lastOpened = new DateField; return true; } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index fd1362848..79c032723 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -14,15 +14,12 @@ import '../DocumentDecorations.scss'; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; -import { DocumentContentsView } from "../nodes/DocumentContentsView"; import { SelectionManager } from "../../util/SelectionManager"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faExpand } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { KeyCodes } from "../../northstar/utils/KeyCodes"; import { undoBatch } from "../../util/UndoManager"; @@ -33,8 +30,8 @@ export interface CellProps { row: number; col: number; rowProps: CellInfo; - CollectionView: Opt; - ContainingCollection: Opt; + CollectionView: Opt; + ContainingCollection: Opt; Document: Doc; fieldKey: string; renderDepth: number; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 670c6bd43..3218f630a 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -23,7 +23,6 @@ import '../DocumentDecorations.scss'; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { undoBatch } from "../../util/UndoManager"; import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders"; @@ -246,8 +245,8 @@ export interface SchemaTableProps { PanelHeight: () => number; PanelWidth: () => number; childDocs?: Doc[]; - CollectionView: Opt; - ContainingCollectionView: Opt; + CollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; fieldKey: string; renderDepth: number; @@ -904,11 +903,11 @@ interface CollectionSchemaPreviewProps { ruleProvider: Doc | undefined; focus?: (doc: Doc) => void; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; - CollectionView?: CollectionView | CollectionVideoView; + CollectionView?: CollectionView; CollectionDoc?: Doc; onClick?: ScriptField; getTransform: () => Transform; - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; removeDocument: (document: Doc) => boolean; active: () => boolean; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5e2b79278..689adc375 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -18,7 +18,6 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import React = require("react"); var path = require('path'); @@ -26,7 +25,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; export interface CollectionViewProps extends FieldViewProps { - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; @@ -37,7 +36,7 @@ export interface CollectionViewProps extends FieldViewProps { } export interface SubCollectionViewProps extends CollectionViewProps { - CollectionView: Opt; + CollectionView: Opt; ruleProvider: Doc | undefined; } @@ -175,14 +174,14 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { DocServer.GetRefField(docid).then(f => { 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); + (f instanceof Doc) && this.props.addDocument(f); } }); } else { this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, options)); } } else if (text) { - this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }), false); + this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text })); } return; } @@ -194,7 +193,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let split = img.split("src=\"")[1].split("\"")[0]; let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 }); ImageUtils.ExtractExif(doc); - this.props.addDocument(doc, false); + this.props.addDocument(doc); return; } else { let path = window.location.origin + "/doc/"; @@ -203,12 +202,12 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { DocServer.GetRefField(docid).then(f => { 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); + (f instanceof Doc) && this.props.addDocument(f); } }); } else { let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }); - this.props.addDocument(htmlDoc, false); + this.props.addDocument(htmlDoc); } return; } @@ -252,7 +251,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let type = result["content-type"]; if (type) { Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 }) - .then(doc => doc && this.props.addDocument(doc, false)); + .then(doc => doc && this.props.addDocument(doc)); } }); promises.push(prom); diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss deleted file mode 100644 index 509851ebb..000000000 --- a/src/client/views/collections/CollectionVideoView.scss +++ /dev/null @@ -1,51 +0,0 @@ - -.collectionVideoView-cont{ - width: 100%; - height: 100%; - position: inherit; - top: 0; - left:0; - z-index: -1; - display:inline-table; -} -.collectionVideoView-time{ - color : white; - top :25px; - left : 25px; - position: absolute; - background-color: rgba(50, 50, 50, 0.2); - transform-origin: left top; -} -.collectionVideoView-snapshot{ - color : white; - top :25px; - right : 25px; - position: absolute; - background-color: rgba(50, 50, 50, 0.2); - transform-origin: left top; -} -.collectionVideoView-play { - width: 25px; - height: 20px; - bottom: 25px; - left : 25px; - position: absolute; - color : white; - background-color: rgba(50, 50, 50, 0.2); - border-radius: 4px; - text-align: center; - transform-origin: left bottom; -} -.collectionVideoView-full { - width: 25px; - height: 20px; - bottom: 25px; - right : 25px; - position: absolute; - color : white; - background-color: rgba(50, 50, 50, 0.2); - border-radius: 4px; - text-align: center; - transform-origin: right bottom; - -} \ No newline at end of file diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx deleted file mode 100644 index 3d898b7de..000000000 --- a/src/client/views/collections/CollectionVideoView.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { action } from "mobx"; -import { observer } from "mobx-react"; -import { NumCast } from "../../../new_fields/Types"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { VideoBox } from "../nodes/VideoBox"; -import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import "./CollectionVideoView.scss"; -import React = require("react"); -import { InkingControl } from "../InkingControl"; -import { InkTool } from "../../../new_fields/InkField"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - - -@observer -export class CollectionVideoView extends React.Component { - private _videoBox?: VideoBox; - - public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") { - return FieldView.LayoutString(CollectionVideoView, fieldKey, fieldExt); - } - private get uIButtons() { - let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); - let curTime = NumCast(this.props.Document.currentTimecode); - return ([
- {"" + Math.round(curTime)} - {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} -
, -
- -
, - VideoBox._showControls ? (null) : [ -
- -
, -
- F -
- ]]); - } - - @action - onPlayDown = () => { - if (this._videoBox) { - if (this._videoBox.Playing) { - this._videoBox.Pause(); - } else { - this._videoBox.Play(); - } - } - } - - @action - onFullDown = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.FullScreen(); - e.stopPropagation(); - e.preventDefault(); - } - } - - @action - onSnapshot = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.Snapshot(); - e.stopPropagation(); - e.preventDefault(); - } - } - - _isclick = 0; - @action - onResetDown = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.Pause(); - e.stopPropagation(); - this._isclick = 0; - document.addEventListener("pointermove", this.onPointerMove, true); - document.addEventListener("pointerup", this.onPointerUp, true); - InkingControl.Instance.switchTool(InkTool.Eraser); - } - } - - @action - onPointerMove = (e: PointerEvent) => { - this._isclick += Math.abs(e.movementX) + Math.abs(e.movementY); - if (this._videoBox) { - this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); - } - e.stopImmediatePropagation(); - } - @action - onPointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.onPointerMove, true); - document.removeEventListener("pointerup", this.onPointerUp, true); - InkingControl.Instance.switchTool(InkTool.None); - this._isclick < 10 && (this.props.Document.currentTimecode = 0); - } - setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; }; - - private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...this.props, ...renderProps }; - return (<> - - {this.props.isSelected() ? this.uIButtons : (null)} - ); - } - - render() { - return ( - - {this.subView} - ); - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index df089eb00..fe92eed10 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -10,7 +10,7 @@ export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; LinkDocs: Doc[]; - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 38488f033..d2644480c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -93,10 +93,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading); } !this.Document.isRuleProvider && (newBox.heading = heading); - this.addDocument(newBox, false); + this.addDocument(newBox); } - private addDocument = (newBox: Doc, allowDuplicates: boolean) => { - let added = this.props.addDocument(newBox, false); + private addDocument = (newBox: Doc) => { + let added = this.props.addDocument(newBox); added && this.bringToFront(newBox); added && this.updateCluster(newBox); return added; @@ -659,7 +659,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (doc instanceof Doc) { const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); doc.x = xx, doc.y = yy; - this.props.addDocument && this.props.addDocument(doc, false); + this.props.addDocument && this.props.addDocument(doc); } } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index eaf65b88c..bb787106c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -24,7 +24,7 @@ interface MarqueeViewProps { getContainerTransform: () => Transform; getTransform: () => Transform; container: CollectionFreeFormView; - addDocument: (doc: Doc, allowDuplicates: false) => boolean; + addDocument: (doc: Doc) => boolean; activeDocuments: () => Doc[]; selectDocuments: (docs: Doc[]) => void; removeDocument: (doc: Doc) => boolean; @@ -83,7 +83,7 @@ export class MarqueeView extends React.Component ns.map(line => { let indent = line.search(/\S|$/); let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); - this.props.addDocument(newBox, false); + this.props.addDocument(newBox); y += 40 * this.props.getTransform().Scale; }); })(); @@ -92,7 +92,7 @@ export class MarqueeView extends React.Component navigator.clipboard.readText().then(text => { let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); if (ns.length === 1 && text.startsWith("http")) { - this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer + this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer } else { this.pasteTable(ns, x, y); } @@ -146,7 +146,7 @@ export class MarqueeView extends React.Component } let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); - this.props.addDocument(newCol, false); + this.props.addDocument(newCol); } } @action @@ -202,7 +202,7 @@ export class MarqueeView extends React.Component } } - setPreviewCursor = (x: number, y: number, drag: boolean) => { + setPreviewCursor = action((x: number, y: number, drag: boolean) => { if (drag) { this._downX = this._lastX = x; this._downY = this._lastY = y; @@ -217,7 +217,7 @@ export class MarqueeView extends React.Component this._downY = y; PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); } - } + }) @action onClick = (e: React.MouseEvent): void => { @@ -350,7 +350,7 @@ export class MarqueeView extends React.Component } } else { - this.props.addDocument(newCollection, false); + this.props.addDocument(newCollection); this.props.selectDocuments([newCollection]); } this.cleanupInteractions(false); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index b18aa5d63..32ebe7c61 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -278,7 +278,7 @@ export class LinkFollowBox extends React.Component { alias.width = width; alias.height = height; - this.sourceView.props.addDocument(alias, false); + this.sourceView.props.addDocument(alias); } } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a824ae9cc..e4b2ecffd 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -9,7 +9,6 @@ import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { LinkFollowBox } from "../linking/LinkFollowBox"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; @@ -100,7 +99,7 @@ export class DocumentContentsView extends React.Component; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; Document: Doc; DataDoc?: Doc; fitToBox?: boolean; onClick?: ScriptField; - addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (doc: Doc) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; @@ -217,7 +216,7 @@ export class DocumentView extends DocComponent(Docu let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace"); maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab")); if (maxLocation === "inPlace") { - expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false)); + expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc)); let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs); } else { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c17730f48..074efaf6c 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -8,7 +8,6 @@ import { List } from "../../../new_fields/List"; import { RichTextField } from "../../../new_fields/RichTextField"; import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField"; import { Transform } from "../../util/Transform"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { AudioBox } from "./AudioBox"; import { FormattedTextBox } from "./FormattedTextBox"; @@ -28,7 +27,7 @@ export interface FieldViewProps { fieldExt: string; leaveNativeSize?: boolean; fitToBox?: boolean; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; ruleProvider: Doc | undefined; Document: Doc; @@ -37,7 +36,7 @@ export interface FieldViewProps { isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; - addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (document: Doc) => boolean; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; removeDocument?: (document: Doc) => boolean; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 1f3887608..57803be1f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -174,7 +174,7 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } - _initialScale: number | undefined; + _initialScale: number | undefined; // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not. Getting bigger after startup won't make it automatically be live.... render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index d651a8621..b3cd439aa 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,6 +1,14 @@ +// .videoBox-container { +// .collectionfreeformview-container { +// mix-blend-mode: multiply; +// } +// } + .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen { width: 100%; + z-index: 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt + position: absolute; } .videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen { @@ -11,7 +19,52 @@ height: 100%; } -.videoBox-content-interactive, .videoBox-content-fullScreen, -.videoBox-content-YouTube-fullScreen { +.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen { pointer-events: all; +} + +.videoBox-time{ + color : white; + top :25px; + left : 25px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); + transform-origin: left top; + pointer-events:all; +} +.videoBox-snapshot{ + color : white; + top :25px; + right : 25px; + position: absolute; + background-color:rgba(50, 50, 50, 0.2); + transform-origin: left top; + pointer-events:all; +} +.videoBox-play { + width: 25px; + height: 20px; + bottom: 25px; + left : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; + transform-origin: left bottom; + pointer-events:all; +} +.videoBox-full { + width: 25px; + height: 20px; + bottom: 25px; + right : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; + transform-origin: right bottom; + pointer-events:all; + } \ No newline at end of file diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index e83aa8bea..feb067d8f 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,11 +3,11 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction, import { observer } from "mobx-react"; import * as rp from 'request-promise'; import { InkTool } from "../../../new_fields/InkField"; -import { makeInterface, createSchema } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types"; +import { makeInterface, createSchema, listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; import { RouteStore } from "../../../server/RouteStore"; -import { Utils } from "../../../Utils"; +import { Utils, emptyFunction, returnOne } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; @@ -22,6 +22,8 @@ import { faVideo } from "@fortawesome/free-solid-svg-icons"; import { Doc } from "../../../new_fields/Doc"; import { ScriptField } from "../../../new_fields/ScriptField"; import { positionSchema } from "./CollectionFreeFormDocumentView"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; var path = require('path'); export const timeSchema = createSchema({ @@ -34,19 +36,24 @@ library.add(faVideo); @observer export class VideoBox extends DocComponent(VideoDocument) { + static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; private _youtubePlayer: YT.Player | undefined = undefined; private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; - static _youtubeIframeCounter: number = 0; + private _downX: number = 0; + private _downY: number = 0; + private _isResetClick = 0; + private _isChildActive = false; + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @observable _forceCreateYouTubeIFrame = false; @observable static _showControls: boolean; @observable _playTimer?: NodeJS.Timeout = undefined; @observable _fullScreen = false; @observable public Playing: boolean = false; - public static LayoutString() { return FieldView.LayoutString(VideoBox); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); } public get player(): HTMLVideoElement | null { return this._videoRef; @@ -123,8 +130,8 @@ export class VideoBox extends DocComponent(VideoD //convert to desired file format var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - let filename = path.basename(encodeURIComponent("snapshot" + this.Document.title + "_" + (this.Document.currentTimecode || 0).toString())); - VideoBox.convertDataUri(dataUrl, filename.replace(/\..*$/, "")).then(returnedFilename => { + let filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_"))); + VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => { if (returnedFilename) { let url = this.choosePath(Utils.prepend(returnedFilename)); let imageSummary = Docs.Create.ImageDocument(url, { @@ -132,7 +139,7 @@ export class VideoBox extends DocComponent(VideoD width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-" }); imageSummary.isButton = true; - this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); + this.props.addDocument && this.props.addDocument(imageSummary); DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "snapshot from " + this.Document.title, "video frame snapshot"); } }); @@ -259,7 +266,73 @@ export class VideoBox extends DocComponent(VideoD }); } + private get uIButtons() { + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); + let curTime = NumCast(this.props.Document.currentTimecode); + return ([
+ {"" + Math.round(curTime)} + {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} +
, +
+ +
, + VideoBox._showControls ? (null) : [ +
+ +
, +
+ F +
+ ]]); + } + + @action + onPlayDown = () => { + if (this.Playing) { + this.Pause(); + } else { + this.Play(); + } + } + @action + onFullDown = (e: React.PointerEvent) => { + this.FullScreen(); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onSnapshot = (e: React.PointerEvent) => { + this.Snapshot(); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onResetDown = (e: React.PointerEvent) => { + this.Pause(); + e.stopPropagation(); + this._isResetClick = 0; + document.addEventListener("pointermove", this.onResetMove, true); + document.addEventListener("pointerup", this.onResetUp, true); + InkingControl.Instance.switchTool(InkTool.Eraser); + } + + @action + onResetMove = (e: PointerEvent) => { + this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY); + this.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); + e.stopImmediatePropagation(); + } + @action + onResetUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onResetMove, true); + document.removeEventListener("pointerup", this.onResetUp, true); + InkingControl.Instance.switchTool(InkTool.None); + this._isResetClick < 10 && (this.props.Document.currentTimecode = 0); + } + @computed get fieldExtensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } @computed get youtubeContent() { @@ -273,11 +346,95 @@ export class VideoBox extends DocComponent(VideoD >; } + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { + this._setPreviewCursor = func; + } + + @action.bound + removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; + //TODO This won't create the field if it doesn't already exist + let targetDataDoc = this.fieldExtensionDoc; + let targetField = this.props.fieldExt; + let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); + let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); + index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); + index !== -1 && value.splice(index, 1); + return true; + } + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + return this.removeDocument(doc) ? addDocument(doc) : false; + } + + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + var curTime = NumCast(this.props.Document.currentTimecode, -1); + curTime !== -1 && (doc.displayTimecode = curTime); + Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); + return true; + } + whenActiveChanged = (isActive: boolean) => { + this._isChildActive = isActive; + this.props.whenActiveChanged(isActive); + } + active = () => { + return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + } + onClick = (e: React.MouseEvent) => { + this._setPreviewCursor && + e.button === 0 && + Math.abs(e.clientX - this._downX) < 3 && + Math.abs(e.clientY - this._downY) < 3 && + this._setPreviewCursor(e.clientX, e.clientY, false); + } + + onPointerDown = (e: React.PointerEvent): void => { + this._downX = e.clientX; + this._downY = e.clientY; + if ((e.button !== 0 || e.altKey) && this.active()) { + this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); + } + } + @computed get annotationLayer() { + return + + } + render() { Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); - return
+ return (
{this.youtubeVideoId ? this.youtubeContent : this.content} -
; + {this.annotationLayer} + {this.uIButtons} +
); } } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 29eef27a0..bb4c5adc9 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -91,7 +91,7 @@ export class WebBox extends React.Component { }); SelectionManager.SelectedDocuments().map(dv => { - dv.props.addDocument && dv.props.addDocument(newBox, false); + dv.props.addDocument && dv.props.addDocument(newBox); dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); }); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 65ca830d9..366861144 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -18,7 +18,6 @@ import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); import * as rp from "request-promise"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -50,10 +49,10 @@ interface IViewerProps { GoToPage?: (n: number) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; - addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (doc: Doc) => boolean; setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; whenActiveChanged: (isActive: boolean) => void; } @@ -582,12 +581,15 @@ export class PDFViewer extends React.Component { } return this.removeDocument(doc) ? addDocument(doc) : false; } - - + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); + return true; + } @action.bound removeDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = undefined; - //TODO This won't create the field if it doesn't already exist let targetDataDoc = this.props.fieldExtensionDoc; let targetField = this.props.fieldExt; let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); @@ -659,8 +661,8 @@ export class PDFViewer extends React.Component { whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} - CollectionView={this.props.ContainingCollectionView} + addDocument={this.addDocument} + CollectionView={undefined} ScreenToLocalTransform={this.scrollXf} ruleProvider={undefined} renderDepth={this.props.renderDepth + 1} -- cgit v1.2.3-70-g09d2 From 21aab2fbf28e9cf6d08366b57d368d120d6813bb Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Oct 2019 16:42:12 -0400 Subject: got rid of all CollectionView wrappers around XxxBox's added DocAnnotatableComponent --- src/client/documents/Documents.ts | 4 +- src/client/util/RichTextSchema.tsx | 4 +- src/client/views/DocComponent.tsx | 50 ++++++- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 8 +- src/client/views/nodes/ImageBox.scss | 8 +- src/client/views/nodes/ImageBox.tsx | 44 +++++-- src/client/views/nodes/PDFBox.tsx | 24 ++-- src/client/views/nodes/VideoBox.scss | 8 +- src/client/views/nodes/VideoBox.tsx | 143 ++++++--------------- src/client/views/nodes/WebBox.tsx | 69 +++++++--- src/client/views/pdf/Annotation.tsx | 8 +- src/client/views/pdf/PDFViewer.tsx | 65 +++------- src/new_fields/Doc.ts | 6 +- 18 files changed, 234 insertions(+), 234 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 22aa74634..0114f82d8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -120,11 +120,11 @@ export namespace Docs { options: { height: 300, backgroundColor: "black" } }], [DocumentType.IMG, { - layout: { view: ImageBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, + layout: { view: ImageBox, ext: anno }, options: {} }], [DocumentType.WEB, { - layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, + layout: { view: WebBox, ext: anno }, options: { height: 300 } }], [DocumentType.COL, { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a5502577b..063686d58 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -784,8 +784,8 @@ export class DashDocView { addDocTab={self._textBox.props.addDocTab} pinToPres={returnFalse} renderDepth={1} - PanelWidth={self._dashDoc![WidthSym]} - PanelHeight={self._dashDoc![HeightSym]} + PanelWidth={self._dashDoc[WidthSym]} + PanelHeight={self._dashDoc[HeightSym]} focus={emptyFunction} backgroundColor={returnEmptyString} parentActive={returnFalse} diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index d6562492f..93e852bef 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; import { Doc } from '../../new_fields/Doc'; -import { computed } from 'mobx'; +import { computed, action } from 'mobx'; +import { Cast } from '../../new_fields/Types'; +import { listSpec } from '../../new_fields/Schema'; export function DocComponent

(schemaCtor: (doc: Doc) => T) { class Component extends React.Component

{ @@ -11,4 +13,50 @@ export function DocComponent

(schemaCtor: (doc: D } } return Component; +} + +interface DocAnnotatableProps { + Document: Doc; + DataDoc?: Doc; + fieldKey: string; + fieldExt: string; + whenActiveChanged: (isActive: boolean) => void; + isSelected: () => boolean, renderDepth: number +} +export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { + class Component extends React.Component

{ + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + @computed + get Document(): T { + return schemaCtor(this.props.Document); + } + _isChildActive = false; + @action.bound + removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; + let value = Cast(this.extensionDoc[this.props.fieldExt], listSpec(Doc), []); + let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1; + return index !== -1 && value.splice(index, 1) ? true : false; + } + + @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } + + @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } + + // if the moved document is already in this overlay collection nothing needs to be done. + // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false; + } + + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + return Doc.AddDocToList(this.extensionDoc, this.props.fieldExt, doc); + } + whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); + active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + } + return Component; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 5c1960d53..6251d7114 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -86,7 +86,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { let parsed = parseInt(value); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index e8627780d..815b28586 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -72,7 +72,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { let parsed = parseInt(value); if (!isNaN(parsed)) { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 689adc375..06d048383 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -38,6 +38,7 @@ export interface CollectionViewProps extends FieldViewProps { export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; ruleProvider: Doc | undefined; + children?: never | (() => JSX.Element[]) | React.ReactNode; } export function CollectionSubView(schemaCtor: (doc: Doc) => T) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d2644480c..adbad5da5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faFileUpload } from "@fortawesome/free-solid-svg-icons"; +import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc"; @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && !this.isAnnotationOverlay) { + if (!e.cancelBubble) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); @@ -339,7 +339,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.props.Document.lockedPosition || this.props.Document.inOverlay || this.isAnnotationOverlay) return; + if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -477,7 +477,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { PanelWidth: layoutDoc[WidthSym], PanelHeight: layoutDoc[HeightSym], ContentScaling: returnOne, - ContainingCollectionView: this.props.CollectionView, + ContainingCollectionView: this.props.ContainingCollectionView, focus: this.focusDocument, backgroundColor: returnEmptyString, parentActive: this.props.active, @@ -681,10 +681,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } - private childViews = () => [ - , - ...this.views - ] + private childViews = () => { + let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; + return [ + , + ...children, + ...this.views, + ]; + } render() { // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap) this.props.Document.fitX = this.contentBounds && this.contentBounds.x; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bb787106c..ecdd02b0f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -217,7 +217,7 @@ export class MarqueeView extends React.Component this._downY = y; PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); } - }) + }); @action onClick = (e: React.MouseEvent): void => { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9ca77cc8b..cbe4945af 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -218,13 +218,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); let res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); - let tr = this._editorView!.state.tr; + let tr = this._editorView.state.tr; let flattened: TextSelection[] = []; res.map(r => r.map(h => flattened.push(h))); let lastSel = Math.min(flattened.length - 1, this._searchIndex); flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark)); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; - this._editorView!.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView()); + this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView()); } } @@ -232,8 +232,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._editorView && (this._editorView as any).docView) { const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); - let end = this._editorView!.state.doc.nodeSize - 2; - this._editorView!.dispatch(this._editorView!.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark)); + let end = this._editorView.state.doc.nodeSize - 2; + this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark)); } } setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => { diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 71d718b39..97d858f58 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -1,9 +1,9 @@ -.imageBox-cont { +.imageBox-cont, .imageBox-cont-interactive { padding: 0vw; - position: relative; + position: absolute; text-align: center; width: 100%; - height: auto; + height: 100%; max-width: 100%; max-height: 100%; pointer-events: none; @@ -11,8 +11,6 @@ .imageBox-cont-interactive { pointer-events: all; - width: 100%; - height: auto; } .imageBox-dot { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a198a0764..9f39eccea 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -13,20 +13,21 @@ import { ComputedField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; import { RouteStore } from '../../../server/RouteStore'; -import { Utils } from '../../../Utils'; +import { Utils, returnOne, emptyFunction } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; -import { DocComponent } from '../DocComponent'; +import { DocAnnotatableComponent } from '../DocComponent'; import { InkingControl } from '../InkingControl'; import { documentSchema } from './DocumentView'; import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); @@ -54,9 +55,10 @@ type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); @observer -export class ImageBox extends DocComponent(ImageDocument) { +export class ImageBox extends DocAnnotatableComponent(ImageDocument) { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(ImageBox, "data", fieldExt); } + @observable static _showControls: boolean; private _imgRef: React.RefObject = React.createRef(); private _downX: number = 0; private _downY: number = 0; @@ -65,10 +67,6 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; @observable private hoverActive = false; - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } - protected createDropTarget = (ele: HTMLDivElement) => { if (this.dropDisposer) { this.dropDisposer(); @@ -381,7 +379,8 @@ export class ImageBox extends DocComponent(ImageD return (null); } - render() { + @computed + get content() { // let transform = this.props.ScreenToLocalTransform().inverse(); let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; // var [sptX, sptY] = transform.transformPoint(0, 0); @@ -393,7 +392,6 @@ export class ImageBox extends DocComponent(ImageD let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; // this._curSuffix = ""; // if (w > 20) { - Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); let alts = DocListCast(this.extensionDoc.Alternates); let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); let field = this.dataDoc[this.props.fieldKey]; @@ -448,4 +446,30 @@ export class ImageBox extends DocComponent(ImageD

); } + + render() { + Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); + return (
+ + {() => [this.content]} + +
); + } } \ No newline at end of file diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 57803be1f..9af6d7cad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,17 +1,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import 'react-image-lightbox/style.css'; -import { Doc, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt, WidthSym } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast } from "../../../new_fields/Types"; import { PdfField } from "../../../new_fields/URLField"; +import { Utils } from '../../../Utils'; import { KeyCodes } from '../../northstar/utils/KeyCodes'; +import { undoBatch } from '../../util/UndoManager'; import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; -import { DocComponent } from "../DocComponent"; +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { DocAnnotatableComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; import { PDFViewer } from "../pdf/PDFViewer"; import { documentSchema } from "./DocumentView"; @@ -19,22 +23,17 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { undoBatch } from '../../util/UndoManager'; -import { ContextMenuProps } from '../ContextMenuItem'; -import { ContextMenu } from '../ContextMenu'; -import { Utils } from '../../../Utils'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer -export class PDFBox extends DocComponent(PdfDocument) { +export class PDFBox extends DocAnnotatableComponent(PdfDocument) { public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); } private _keyValue: string = ""; private _valueValue: string = ""; private _scriptValue: string = ""; private _searchString: string = ""; - private _isChildActive = false; private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title private _pdfViewer: PDFViewer | undefined; private _keyRef: React.RefObject = React.createRef(); @@ -46,9 +45,6 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _pdf: Opt; @observable private _pageControls = false; - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } - componentDidMount() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); if (pdfUrl instanceof PdfField) { @@ -206,7 +202,7 @@ export class PDFBox extends DocComponent(PdfDocumen pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} extensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index b3cd439aa..48623eaaf 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,8 +1,6 @@ -// .videoBox-container { -// .collectionfreeformview-container { -// mix-blend-mode: multiply; -// } -// } +.videoBox-container { + pointer-events: all; +} .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index feb067d8f..aa9b28118 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -11,7 +11,7 @@ import { Utils, emptyFunction, returnOne } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { DocComponent } from "../DocComponent"; +import { DocAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { documentSchema } from "./DocumentView"; @@ -35,7 +35,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); library.add(faVideo); @observer -export class VideoBox extends DocComponent(VideoDocument) { +export class VideoBox extends DocAnnotatableComponent(VideoDocument) { static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; @@ -43,16 +43,12 @@ export class VideoBox extends DocComponent(VideoD private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; - private _downX: number = 0; - private _downY: number = 0; private _isResetClick = 0; - private _isChildActive = false; - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @observable _forceCreateYouTubeIFrame = false; - @observable static _showControls: boolean; @observable _playTimer?: NodeJS.Timeout = undefined; @observable _fullScreen = false; - @observable public Playing: boolean = false; + @observable _playing = false; + @observable static _showControls: boolean; public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); } public get player(): HTMLVideoElement | null { @@ -72,7 +68,7 @@ export class VideoBox extends DocComponent(VideoD } @action public Play = (update: boolean = true) => { - this.Playing = true; + this._playing = true; update && this.player && this.player.play(); update && this._youtubePlayer && this._youtubePlayer.playVideo(); this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5)); @@ -85,7 +81,7 @@ export class VideoBox extends DocComponent(VideoD } @action public Pause = (update: boolean = true) => { - this.Playing = false; + this._playing = false; update && this.player && this.player.pause(); update && this._youtubePlayer && this._youtubePlayer.pauseVideo && this._youtubePlayer.pauseVideo(); this._youtubePlayer && this._playTimer && clearInterval(this._playTimer); @@ -181,7 +177,7 @@ export class VideoBox extends DocComponent(VideoD vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); this._reactionDisposer && this._reactionDisposer(); this._reactionDisposer = reaction(() => this.Document.currentTimecode || 0, - time => !this.Playing && (vref.currentTime = time), { fireImmediately: true }); + time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } @@ -218,7 +214,7 @@ export class VideoBox extends DocComponent(VideoD let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"; let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; return !field ?
Loading
: -
, VideoBox._showControls ? (null) : [
- +
, -
+
F
]]); } @action - onPlayDown = () => { - if (this.Playing) { - this.Pause(); - } else { - this.Play(); - } - } + onPlayDown = () => this._playing ? this.Pause() : this.Play() @action onFullDown = (e: React.PointerEvent) => { @@ -342,97 +332,40 @@ export class VideoBox extends DocComponent(VideoD let start = untracked(() => Math.round(this.Document.currentTimecode || 0)); return ; - } - - - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { - this._setPreviewCursor = func; + src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />; } @action.bound - removeDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = undefined; - //TODO This won't create the field if it doesn't already exist - let targetDataDoc = this.fieldExtensionDoc; - let targetField = this.props.fieldExt; - let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); - let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); - index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - index !== -1 && value.splice(index, 1); - return true; - } - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - return this.removeDocument(doc) ? addDocument(doc) : false; - } - - @action.bound - addDocument(doc: Doc): boolean { + addDocumentWithTimestamp(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = this.props.Document; var curTime = NumCast(this.props.Document.currentTimecode, -1); curTime !== -1 && (doc.displayTimecode = curTime); - Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); - return true; - } - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - active = () => { - return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; - } - onClick = (e: React.MouseEvent) => { - this._setPreviewCursor && - e.button === 0 && - Math.abs(e.clientX - this._downX) < 3 && - Math.abs(e.clientY - this._downY) < 3 && - this._setPreviewCursor(e.clientX, e.clientY, false); - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - if ((e.button !== 0 || e.altKey) && this.active()) { - this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); - } - } - @computed get annotationLayer() { - return - + return Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); } render() { Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); - return (
- {this.youtubeVideoId ? this.youtubeContent : this.content} - {this.annotationLayer} + return (
+ + {() => [this.youtubeVideoId ? this.youtubeContent : this.content]} + {this.uIButtons}
); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index bb4c5adc9..7c7f9fb83 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,34 +1,36 @@ +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { FieldResult, Doc, Field } from "../../../new_fields/Doc"; +import { Doc, FieldResult } from "../../../new_fields/Doc"; import { HtmlField } from "../../../new_fields/HtmlField"; +import { InkTool } from "../../../new_fields/InkField"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; import { WebField } from "../../../new_fields/URLField"; +import { emptyFunction, returnOne, Utils } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { SelectionManager } from "../../util/SelectionManager"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; +import { KeyValueBox } from "./KeyValueBox"; import "./WebBox.scss"; import React = require("react"); -import { InkTool } from "../../../new_fields/InkField"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils } from "../../../Utils"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; -import { observable, action, computed } from "mobx"; -import { listSpec } from "../../../new_fields/Schema"; -import { RefField } from "../../../new_fields/RefField"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { updateSourceFile } from "typescript"; -import { KeyValueBox } from "./KeyValueBox"; -import { setReactionScheduler } from "mobx/lib/internal"; -import { library } from "@fortawesome/fontawesome-svg-core"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Docs } from "../../documents/Documents"; +import { documentSchema } from "./DocumentView"; +import { DocAnnotatableComponent } from "../DocComponent"; library.add(faStickyNote); +type WebDocument = makeInterface<[typeof documentSchema]>; +const WebDocument = makeInterface(documentSchema); + @observer -export class WebBox extends React.Component { +export class WebBox extends DocAnnotatableComponent(WebDocument) { - public static LayoutString() { return FieldView.LayoutString(WebBox); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(WebBox, "data", fieldExt); } @observable private collapsed: boolean = true; @observable private url: string = ""; @@ -162,8 +164,10 @@ export class WebBox extends React.Component { e.stopPropagation(); } } - render() { - let field = this.props.Document[this.props.fieldKey]; + + @computed + get content() { + let field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { view = ; @@ -189,4 +193,29 @@ export class WebBox extends React.Component { {!frozen ? (null) :
} ); } + render() { + Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); + return (
+ + {() => [this.content]} + +
); + } } \ No newline at end of file diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 5a07b88d9..ad6240c70 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -11,7 +11,7 @@ import "./Annotation.scss"; interface IAnnotationProps { anno: Doc; - fieldExtensionDoc: Doc; + extensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Opt, where: string) => boolean; pinToPres: (document: Doc) => void; focus: (doc: Doc) => void; @@ -29,7 +29,7 @@ interface IRegionAnnotationProps { y: number; width: number; height: number; - fieldExtensionDoc: Doc; + extensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; document: Doc; @@ -66,12 +66,12 @@ class RegionAnnotation extends React.Component { } deleteAnnotation = () => { - let annotation = DocListCast(this.props.fieldExtensionDoc.annotations); + let annotation = DocListCast(this.props.extensionDoc.annotations); let group = FieldValue(Cast(this.props.document.group, Doc)); if (group) { if (annotation.indexOf(group) !== -1) { let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc))); - this.props.fieldExtensionDoc.annotations = new List(newAnnotations); + this.props.extensionDoc.annotations = new List(newAnnotations); } DocListCast(group.annotations).forEach(anno => anno.delete = true); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 366861144..d0759d207 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,7 +6,7 @@ import { Dictionary } from "typescript-collections"; import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; +import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from "../../../Utils"; @@ -23,19 +23,24 @@ import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; +import { DocAnnotatableComponent } from "../DocComponent"; +import { documentSchema } from "../nodes/DocumentView"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; +type PdfDocument = makeInterface<[typeof documentSchema]>; +const PdfDocument = makeInterface(documentSchema); interface IViewerProps { pdf: Pdfjs.PDFDocumentProxy; url: string; - Document: Doc; - DataDoc?: Doc; - fieldExtensionDoc: Doc; fieldKey: string; fieldExt: string; + Document: Doc; + DataDoc?: Doc; + ContainingCollectionView: Opt; + extensionDoc: Doc; PanelWidth: () => number; PanelHeight: () => number; ContentScaling: () => number; @@ -52,7 +57,6 @@ interface IViewerProps { addDocument?: (doc: Doc) => boolean; setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; - ContainingCollectionView: Opt; whenActiveChanged: (isActive: boolean) => void; } @@ -60,7 +64,7 @@ interface IViewerProps { * Handles rendering and virtualization of the pdf */ @observer -export class PDFViewer extends React.Component { +export class PDFViewer extends DocAnnotatableComponent(PdfDocument) { static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @@ -79,7 +83,6 @@ export class PDFViewer extends React.Component { private _pdfViewer: any; private _retries = 0; // number of times tried to create the PDF viewer - private _isChildActive = false; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; @@ -97,7 +100,7 @@ export class PDFViewer extends React.Component { private _coverPath: any; @computed get allAnnotations() { - return DocListCast(this.props.fieldExtensionDoc.annotations).filter( + return DocListCast(this.props.extensionDoc.annotations).filter( anno => this._script.run({ this: anno }, console.log, true).result); } @@ -186,7 +189,7 @@ export class PDFViewer extends React.Component { await this.initialLoad(); this._annotationReactionDisposer = reaction( - () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), + () => this.props.extensionDoc && DocListCast(this.props.extensionDoc.annotations), annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), { fireImmediately: true }); @@ -530,7 +533,7 @@ export class PDFViewer extends React.Component { highlight = (color: string) => { // creates annotation documents for current highlights let annotationDoc = this.makeAnnotationDocument(color); - annotationDoc && Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); + annotationDoc && Doc.AddDocToList(this.props.extensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -570,40 +573,9 @@ export class PDFViewer extends React.Component { DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0); } - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - return this.removeDocument(doc) ? addDocument(doc) : false; - } - @action.bound - addDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = this.props.Document; - Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); - return true; - } - @action.bound - removeDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = undefined; - let targetDataDoc = this.props.fieldExtensionDoc; - let targetField = this.props.fieldExt; - let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); - let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); - index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - index !== -1 && value.splice(index, 1); - return true; - } scrollXf = () => { return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform(); } - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { - this._setPreviewCursor = func; - } onClick = (e: React.MouseEvent) => { this._setPreviewCursor && e.button === 0 && @@ -611,13 +583,9 @@ export class PDFViewer extends React.Component { Math.abs(e.clientY - this._downY) < 3 && this._setPreviewCursor(e.clientX, e.clientY, false); } - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - active = () => { - return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; - } + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + getCoverImage = () => { if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { @@ -632,7 +600,6 @@ export class PDFViewer extends React.Component { style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; } - @action onZoomWheel = (e: React.WheelEvent) => { e.stopPropagation(); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 418863bcc..eb752f8c6 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -332,8 +332,10 @@ export namespace Doc { return Array.from(results); } - export function IndexOf(toFind: Doc, list: Doc[]) { - return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); + export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) { + let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1); + index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1); + return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); } export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) { if (listDoc[key] === undefined) { -- cgit v1.2.3-70-g09d2 From d95d46e90851141bed17dd30c103be5d0e1e60ab Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 11 Oct 2019 13:43:48 -0400 Subject: search fixes for pdfs --- src/client/views/GlobalKeyHandler.ts | 2 ++ src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 36 ++++++++++++++++++++-- src/client/views/nodes/QueryBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 7 +++-- src/server/index.ts | 1 - 6 files changed, 43 insertions(+), 7 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 6815ff926..557b3e366 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -163,6 +163,8 @@ export default class KeyManager { } break; case "f": + stopPropagation = false; + preventDefault = false; MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible; MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0; PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0))); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 06d048383..fdbe5339d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -275,7 +275,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName }; let pathname = Utils.prepend(file.path); Docs.Get.DocumentFromType(type, pathname, full).then(doc => { - doc && (doc.fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "")); + doc && (Doc.GetProto(doc).fileUpload = path.basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "")); doc && this.props.addDocument(doc); }); })); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 9af6d7cad..02a82e0ed 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, runInAction } from 'mobx'; +import { action, observable, runInAction, reaction, IReactionDisposer } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; @@ -36,20 +36,34 @@ export class PDFBox extends DocAnnotatableComponent private _searchString: string = ""; private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title private _pdfViewer: PDFViewer | undefined; + private _searchRef: React.RefObject = React.createRef(); private _keyRef: React.RefObject = React.createRef(); private _valueRef: React.RefObject = React.createRef(); private _scriptRef: React.RefObject = React.createRef(); + private _selectReaction: IReactionDisposer | undefined; @observable private _searching: boolean = false; @observable private _flyout: boolean = false; @observable private _pdf: Opt; @observable private _pageControls = false; + componentWillUnmount() { + this._selectReaction && this._selectReaction(); + } componentDidMount() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); if (pdfUrl instanceof PdfField) { Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf)); } + this._selectReaction = reaction(() => this.props.isSelected(), + () => { + if (this.props.isSelected()) { + document.removeEventListener("keydown", this.onKeyDown); + document.addEventListener("keydown", this.onKeyDown); + } else { + document.removeEventListener("keydown", this.onKeyDown); + } + }, { fireImmediately: true }); } loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; @@ -65,6 +79,22 @@ export class PDFBox extends DocAnnotatableComponent public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); }; public forwardPage() { this._pdfViewer!.gotoPage((this.Document.curPage || 1) + 1); } + @undoBatch + onKeyDown = action((e: KeyboardEvent) => { + if (e.key === "f" && e.ctrlKey) { + this._searching = true; + setTimeout(() => this._searchRef.current && this._searchRef.current.focus(), 100); + e.stopImmediatePropagation(); + e.preventDefault(); + } + if (e.key === "PageDown" || e.key === "ArrowDown" || e.key === "ArrowRight") { + this.forwardPage(); + } + if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") { + this.backPage(); + } + }) + @undoBatch @action private applyFilter = () => { @@ -109,7 +139,9 @@ export class PDFBox extends DocAnnotatableComponent onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none", position: "absolute", width: "100%", height: "100%", zIndex: 1, pointerEvents: "none" }}>
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
; } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d0759d207..1bae6128c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -126,7 +126,9 @@ export class PDFViewer extends DocAnnotatableComponent this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), + () => (SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), + { fireImmediately: true }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -655,7 +657,8 @@ export class PDFViewer extends DocAnnotatableComponent this._marqueeing; visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96; render() { - return (
+ return (
{this.pdfViewerDiv} {this.annotationLayer} {this.standinViews} diff --git a/src/server/index.ts b/src/server/index.ts index 62938b9c7..86c226a21 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -213,7 +213,6 @@ const solrURL = "http://localhost:8983/solr/#/dash"; app.get("/textsearch", async (req, res) => { let q = req.query.q; - console.log("TEXTSEARCH " + q); if (q === undefined) { res.send([]); return; -- cgit v1.2.3-70-g09d2 From 11537da75c76fba79a2709d2ad175dfa16a25256 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 18 Oct 2019 14:00:25 -0400 Subject: fixes for drawing link anchors to pdf text selections. --- src/client/documents/DocumentTypes.ts | 3 ++- src/client/util/DocumentManager.ts | 9 +++++---- src/client/util/LinkManager.ts | 19 +++++++------------ src/client/views/MainView.tsx | 3 ++- src/client/views/nodes/DocuLinkBox.tsx | 7 +++++-- src/client/views/nodes/DocumentView.scss | 3 --- src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 6 ++++++ 9 files changed, 30 insertions(+), 24 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index ea37fc2f1..12501065a 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -23,5 +23,6 @@ export enum DocumentType { PRESELEMENT = "preselement", QUERY = "search", COLOR = "color", - DOCULINK = "doculink" + DOCULINK = "doculink", + PDFANNO = "pdfanno" } \ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ee6772f8f..0f2a47dd0 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -100,10 +100,11 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document) - || DocumentManager.Instance.DocumentViews.some(dv2 => { - let init = dv2.isSelected() || Doc.IsBrushed(dv2.props.Document); - let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document)); + let pairs = DocumentManager.Instance.DocumentViews.filter(dv => + dv.isSelected() || Doc.IsBrushed(dv.props.Document) // draw links from DocumentViews that are selected or brushed OR + || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which + let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors + let init = dv2.isSelected() || Doc.IsBrushed(dv2.props.Document); // on a view that is selected or brushed return init && rest; }) ).reduce((pairs, dv) => { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 35c0f023f..ee2f2dadc 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,10 +1,7 @@ -import { observable, action } from "mobx"; -import { StrCast, Cast, FieldValue } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; -import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; -import { Id } from "../../new_fields/FieldSymbols"; -import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { listSpec } from "../../new_fields/Schema"; +import { Cast, StrCast } from "../../new_fields/Types"; import { Docs } from "../documents/Documents"; import { Scripting } from "./Scripting"; @@ -242,13 +239,11 @@ export class LinkManager { //TODO This should probably return undefined if there isn't an opposite anchor //TODO This should also await the return value of the anchor so we don't filter out promises public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { - return Cast(linkDoc.anchor2, Doc, null); - } else if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor2, Doc, null))) { - return Cast(linkDoc.anchor1, Doc, null); - } else if (Doc.AreProtosEqual(anchor, linkDoc)) { - return linkDoc; - } + let a1 = Cast(linkDoc.anchor1, Doc, null); + let a2 = Cast(linkDoc.anchor2, Doc, null); + if (Doc.AreProtosEqual(anchor, a1)) return a2; + if (Doc.AreProtosEqual(anchor, a2)) return a1; + if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc; } } Scripting.addGlobal(function links(doc: any) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9304f4bef..4c2b6f262 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -183,7 +183,8 @@ export class MainView extends React.Component { y: 400, width: this._panelWidth * .7, height: this._panelHeight, - title: "My Blank Collection" + title: "My Blank Collection", + backgroundColor: "white" }; let workspaces: FieldResult; let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index f56bf4ad3..3294a5aa2 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -2,7 +2,7 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { Utils } from '../../../Utils'; import { DocumentManager } from "../../util/DocumentManager"; import { DragLinksAsDocuments } from "../../util/DragManager"; @@ -11,6 +11,7 @@ import { documentSchema } from "./DocumentView"; import "./DocuLinkBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); +import { DocumentType } from "../../documents/DocumentTypes"; type DocLinkSchema = makeInterface<[typeof documentSchema]>; const DocLinkDocument = makeInterface(documentSchema); @@ -65,13 +66,15 @@ export class DocuLinkBox extends DocComponent(Doc e.stopPropagation(); } render() { + let anchorDoc = Cast(this.props.Document[this.props.fieldKey], Doc); + let hasAnchor = anchorDoc instanceof Doc && anchorDoc.type === DocumentType.PDFANNO; let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100); let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100); let c = StrCast(this.props.Document.backgroundColor, "lightblue"); return
; } } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 69eb1a843..a0bf74990 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -47,9 +47,6 @@ } } } -.documentView-node-topmost { - background: white; -} .documentView-styleWrapper { position: absolute; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 48ad7a632..8bf698391 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -629,6 +629,7 @@ export class DocumentView extends DocComponent(Docu DataDoc={this.props.DataDoc} />); } linkEndpoint = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; + linkEndpointDoc = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor1, Doc) as Doc : Cast(linkDoc.anchor2, Doc) as Doc; render() { if (!this.props.Document) return (null); @@ -701,6 +702,7 @@ export class DocumentView extends DocComponent(Docu onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => + //this.linkEndpointDoc(d).type === DocumentType.PDFANNO ? (null) :
)} diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index ad6240c70..e0a3b9171 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -100,7 +100,7 @@ class RegionAnnotation extends React.Component { let annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { DocumentManager.Instance.FollowLink(undefined, annoGroup, - (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"), + (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"), false, false, undefined); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1bae6128c..6e5f1a981 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -25,6 +25,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; import { DocAnnotatableComponent } from "../DocComponent"; import { documentSchema } from "../nodes/DocumentView"; +import { DocumentType } from "../../documents/DocumentTypes"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -249,6 +250,7 @@ export class PDFViewer extends DocAnnotatableComponent(annoDocs); } mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); -- cgit v1.2.3-70-g09d2 From d8f12dade0d0b336e4328fa1f00a1a538588d34e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 20 Oct 2019 13:34:25 -0400 Subject: initial version of template refactoring --- src/client/documents/Documents.ts | 10 +-- src/client/views/DocumentDecorations.tsx | 26 ++++---- src/client/views/TemplateMenu.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 23 +++---- .../views/collections/CollectionSchemaView.tsx | 13 ++-- .../views/collections/CollectionStackingView.tsx | 37 +++++++---- src/client/views/collections/CollectionSubView.tsx | 11 ++-- .../views/collections/CollectionTreeView.tsx | 19 +++--- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 11 ++-- src/client/views/nodes/DocumentView.tsx | 77 +++++++--------------- src/client/views/nodes/FormattedTextBox.tsx | 3 +- src/client/views/nodes/ImageBox.tsx | 52 ++++++++------- src/client/views/nodes/PDFBox.tsx | 10 +-- src/client/views/nodes/VideoBox.tsx | 35 +++++----- src/client/views/nodes/WebBox.tsx | 11 ++-- src/client/views/pdf/PDFViewer.tsx | 40 +++++------ .../views/presentationview/PresElementBox.tsx | 9 +-- src/client/views/search/SearchBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 2 +- src/new_fields/Doc.ts | 37 +++++------ .../authentication/models/current_user_utils.ts | 2 +- 24 files changed, 211 insertions(+), 231 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 32fda1954..fbf45816a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -300,7 +300,7 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "dropAction", "forceActive"]; + const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "forceActive", "fitWidth"]; /** * This function receives the relevant document prototype and uses @@ -369,11 +369,11 @@ export namespace Docs { requestImageSize(target) .then((size: any) => { let aspect = size.height / size.width; - if (!inst.proto!.nativeWidth) { - inst.proto!.nativeWidth = size.width; + if (!inst.nativeWidth) { + inst.nativeWidth = size.width; } - inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; - inst.proto!.height = NumCast(inst.proto!.width) * aspect; + inst.nativeHeight = NumCast(inst.nativeWidth) * aspect; + inst.height = NumCast(inst.width) * aspect; }) .catch((err: any) => console.log(err)); // } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 07af4799b..252f90d46 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -19,7 +19,7 @@ import { CollectionView } from "./collections/CollectionView"; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { PositionDocument } from './nodes/CollectionFreeFormDocumentView'; -import { DocumentView, swapViews } from "./nodes/DocumentView"; +import { DocumentView } from "./nodes/DocumentView"; import { FieldView } from "./nodes/FieldView"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { IconBox } from "./nodes/IconBox"; @@ -107,10 +107,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> fieldTemplate.title = metaKey; Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); if (text.startsWith(">>")) { - let layoutNative = Doc.MakeTitled("layoutNative"); - Doc.GetProto(docTemplate).layoutNative = layoutNative; - swapViews(fieldTemplate, "", "layoutNative", layoutNative); - layoutNative.layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); + Doc.GetProto(docTemplate).layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } } } @@ -471,8 +468,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); let layoutDoc = PositionDocument(Doc.Layout(element.props.Document)); - let nwidth = doc.nativeWidth || 0; - let nheight = doc.nativeHeight || 0; + let nwidth = layoutDoc.nativeWidth || 0; + let nheight = layoutDoc.nativeHeight || 0; let width = (layoutDoc.width || 0); let height = (layoutDoc.height || (nheight / nwidth * width)); let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling(); @@ -480,21 +477,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - let proto = doc.isTemplateField ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; - proto.nativeWidth = nwidth = layoutDoc.width || 0; - proto.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; } if (fixedAspect && (!nwidth || !nheight)) { - proto.nativeWidth = nwidth = layoutDoc.width || 0; - proto.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; } if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { - Doc.SetInPlace(doc, "nativeWidth", actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0), true); + layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); } layoutDoc.width = actualdW; if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; @@ -502,7 +498,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { if (!fixedAspect) { - Doc.SetInPlace(doc, "nativeHeight", actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0), true); + layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); } layoutDoc.height = actualdH; if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; @@ -511,7 +507,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { dW && (layoutDoc.width = actualdW); dH && (layoutDoc.height = actualdH); - dH && layoutDoc.autoHeight && Doc.SetInPlace(layoutDoc, "autoHeight", false, true); + dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } }); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index d76b033f0..96265385e 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -9,6 +9,7 @@ import { DocumentView } from "./nodes/DocumentView"; import { Template, Templates } from "./Templates"; import React = require("react"); import { Doc } from "../../new_fields/Doc"; +import { StrCast } from "../../new_fields/Types"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -128,7 +129,7 @@ export class TemplateMenu extends React.Component { this.props.templates.forEach((checked, template) => templateMenu.push()); templateMenu.push(); - templateMenu.push(); + templateMenu.push(); templateMenu.push(); return (
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1f78c8c97..42d372f4a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -615,19 +615,20 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth; + get layoutDoc() { return this._document && Doc.Layout(this._document);} + panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc.width), NumCast(this.layoutDoc.nativeWidth)), this._panelWidth) : this._panelWidth; panelHeight = () => this._panelHeight; - nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; + nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { - if (this._document!.type === DocumentType.PDF) { - if ((this._document && this._document.fitWidth) || - this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) { - return this._panelWidth / NumCast(this._document!.nativeWidth); + if (this.layoutDoc!.type === DocumentType.PDF) { + if ((this.layoutDoc && this.layoutDoc.fitWidth) || + this._panelHeight / NumCast(this.layoutDoc!.nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!.nativeWidth)) { + return this._panelWidth / NumCast(this.layoutDoc!.nativeWidth); } else { - return this._panelHeight / NumCast(this._document!.nativeHeight); + return this._panelHeight / NumCast(this.layoutDoc!.nativeHeight); } } const nativeH = this.nativeHeight(); @@ -645,7 +646,7 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } + get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { SelectionManager.DeselectAll(); @@ -690,11 +691,11 @@ export class DockedFrameRenderer extends React.Component { } render() { - return (!this._isActive || !this._document) ? (null) : + return (!this._isActive || !this.layoutDoc) ? (null) : (
this._mainCont = ref} style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this._document && this._document.fitWidth ? undefined : "100%" + height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%" }}> {this.docView}
); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 3218f630a..23e070750 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -922,8 +922,9 @@ interface CollectionSchemaPreviewProps { export class CollectionSchemaPreview extends React.Component{ private dropDisposer?: DragManager.DragDropDisposer; _mainCont?: HTMLDivElement; - private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.PanelWidth()); } - private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.PanelHeight()); } + private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); } + private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); } + private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); } private contentScaling = () => { let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth()); if (wscale * this.nativeHeight > this.props.PanelHeight()) { @@ -947,10 +948,8 @@ export class CollectionSchemaPreview extends React.Component { let target = Doc.GetProto(otherdoc); - let layoutNative = Doc.MakeTitled("layoutNative"); - layoutNative.layout = ComputedField.MakeFunction("this.image_data[0]"); - target.layoutNative = layoutNative; - target.layoutCUstom = target.layout = Doc.MakeDelegate(de.data.draggedDocuments[0]); + target.layout = ComputedField.MakeFunction("this.image_data[0]"); + target.layoutCustom = Doc.MakeDelegate(de.data.draggedDocuments[0]); }); e.stopPropagation(); } @@ -968,7 +967,7 @@ export class CollectionSchemaPreview extends React.Component doc) { this._docXfs.length = 0; return docs.map((d, i) => { let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); - let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); - let height = () => this.getDocHeight(pair.layout); + let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d; + let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + let height = () => this.getDocHeight(layoutDoc); let dref = React.createRef(); - let dxf = () => this.getDocTransform(pair.layout!, dref.current!); + let dxf = () => this.getDocTransform(layoutDoc, dref.current!); this._docXfs.push({ dxf: dxf, width: width, height: height }); let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` }; return
- {pair.layout instanceof Doc && this.getDisplayDoc(pair.layout, pair.data, dxf, width)} + {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
; }); } @@ -111,8 +112,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (this.props.Document.autoHeight) { let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]); if (this.isStackingView) { - return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght, - (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)), 0); + let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => { + let r1 = Math.max(maxHght, + (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => { + let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap); + return val; + }, this.yMargin)); + return r1; + }, 0); + return res; } else { let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0); return this.props.ContentScaling() * (sum + (this.Sections.size ? 85 : -15)); @@ -122,7 +130,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { }, (hgt: number) => { let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; - doc && (doc.height = hgt); + doc && (Doc.Layout(doc).height = hgt); }, { fireImmediately: true } ); @@ -155,11 +163,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); } - getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { - let height = () => this.getDocHeight(layoutDoc); + getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { + let layoutDoc = Doc.Layout(doc); + let height = () => this.getDocHeight(doc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); return doc) { getDocHeight(d?: Doc) { if (!d) return 0; let layoutDoc = Doc.Layout(d); - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); + let nw = NumCast(layoutDoc.nativeWidth); + let nh = NumCast(layoutDoc.nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) { let aspect = nw && nh ? nh / nw : 1; if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid); return wid * aspect; } - return layoutDoc.fitWidth ? !d.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : - Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(d.nativeHeight)) / NumCast(d.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym](); + return layoutDoc.fitWidth ? !layoutDoc.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : + Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym](); } columnDividerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f76447157..0e8c46d40 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -61,11 +61,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], async (args) => { if (args[1] instanceof Doc) { - this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))); + this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), "layoutFromParent")); + } + else if (!(args[1] instanceof Promise)) { + this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout")); } - // else if (!(args[1] instanceof Promise)) { - // this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layout = undefined); - // } }); } @@ -134,8 +134,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) { if (de.mods === "AltKey" && de.data.draggedDocuments.length) { this.childDocs.map(doc => - Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc) - ); + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent")); e.stopPropagation(); return true; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2fbe8527e..77736b7d0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -249,23 +249,22 @@ class TreeView extends React.Component { let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); return finalXf; } + get layoutDoc() { return Doc.Layout(this.props.document); } docWidth = () => { - let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); - let layoutDoc = Doc.Layout(this.props.document); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); - return NumCast(this.props.document.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; + let aspect = NumCast(this.layoutDoc.nativeHeight) / NumCast(this.layoutDoc.nativeWidth); + if (aspect) return Math.min(this.layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); + return NumCast(this.layoutDoc.nativeWidth) ? Math.min(this.layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; } docHeight = () => { let bounds = this.boundsOfCollectionDocument; return Math.min(this.MAX_EMBED_HEIGHT, (() => { - let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); - let layoutDoc = Doc.Layout(this.props.document); + let aspect = NumCast(this.layoutDoc.nativeHeight) / NumCast(this.layoutDoc.nativeWidth); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(this.props.document.nativeHeight)) / NumCast(this.props.document.nativeWidth, + return this.layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : + Math.min(this.docWidth() * NumCast(this.layoutDoc.scrollHeight, NumCast(this.layoutDoc.nativeHeight)) / NumCast(this.layoutDoc.nativeWidth, NumCast(this.props.containingCollection.height)))) : - NumCast(layoutDoc.height) ? NumCast(layoutDoc.height) : 50; + NumCast(this.layoutDoc.height) ? NumCast(this.layoutDoc.height) : 50; })()); } @@ -484,7 +483,7 @@ class TreeView extends React.Component { }; const childLayout = Doc.Layout(pair.layout); let rowHeight = () => { - let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); + let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0); return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); }; return = numCols) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eff73b14e..aa1106f13 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -63,10 +63,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _hitCluster = false; @observable _clusterSets: (Doc[])[] = []; + get layoutDoc() { return PanZoomDocument(Doc.Layout(this.props.Document)); } @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } @computed get contentBounds() { return aggregateBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); } - @computed get nativeWidth() { return this.fitToContent ? 0 : this.Document.nativeWidth || 0; } + @computed get nativeWidth() { return this.layoutDoc.fitToContent ? 0 : this.layoutDoc.nativeWidth || 0; } @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; } private get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt') private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } @@ -448,6 +449,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ...this.props, DataDoc: childData, Document: childLayout, + layoutKey: undefined, ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 2243a44d5..e3ca02fa4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -77,7 +77,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; - let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout : undefined; + let ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined; let br = StrCast((ld || this.props.Document).borderRounding); br = !br && ruleRounding ? ruleRounding : br; if (br.endsWith("%")) { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index bb9315ca3..7aed416f4 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -2,7 +2,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast } from "../../../new_fields/Types"; +import { Cast, StrCast } from "../../../new_fields/Types"; import { OmitKeys, Without } from "../../../Utils"; import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; @@ -69,10 +69,9 @@ export class DocumentContentsView extends React.Component obj.active = this.props.parentActive).omit, Document: this.layoutDoc, - DataDoc: this.dataDoc + DataDoc: this.dataDoc, }; return { props: list }; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1087c57ad..2d13eeae4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -191,9 +191,8 @@ export class DocumentView extends DocComponent(Docu let preventDefault = true; if (this._doubleTap && this.props.renderDepth && (!this.onClickHandler || !this.onClickHandler.script)) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click let fullScreenAlias = Doc.MakeAlias(this.props.Document); - let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc)); - if (layoutNative && fullScreenAlias.layout === layoutNative.layout) { - await swapViews(fullScreenAlias, "layoutCustom", "layoutNative"); + if (StrCast(fullScreenAlias.layoutKey) !== "layoutCustom" && fullScreenAlias["layoutCustom"] !== undefined) { + fullScreenAlias.layoutKey = "layoutCustom"; } this.props.addDocTab(fullScreenAlias, undefined, "inTab"); SelectionManager.DeselectAll(); @@ -287,16 +286,13 @@ export class DocumentView extends DocComponent(Docu @undoBatch deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } - static makeNativeViewClicked = async (doc: Doc): Promise => { - undoBatch(() => swapViews(doc, "layoutNative", "layoutCustom"))(); + static makeNativeViewClicked = (doc: Doc) => { + undoBatch(() => doc.layoutKey = "layout")(); } - static makeCustomViewClicked = async (doc: Doc, dataDoc: Opt) => { + static makeCustomViewClicked = (doc: Doc, dataDoc: Opt) => { const batch = UndoManager.StartBatch("CustomViewClicked"); if (doc.layoutCustom === undefined) { - Doc.GetProto(dataDoc || doc).layoutNative = Doc.MakeTitled("layoutNative"); - await swapViews(doc, "", "layoutNative"); - const width = NumCast(doc.width); const height = NumCast(doc.height); const options = { title: "data", width, x: -width / 2, y: - height / 2, }; @@ -326,10 +322,9 @@ export class DocumentView extends DocComponent(Docu let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) }); Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true); - Doc.ApplyTemplateTo(docTemplate, doc, undefined); - Doc.GetProto(dataDoc || doc).layoutCustom = Doc.MakeTitled("layoutCustom"); + Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined); } else { - await swapViews(doc, "layoutCustom", "layoutNative"); + doc.layoutKey = "layoutCustom"; } batch.end(); } @@ -356,7 +351,7 @@ export class DocumentView extends DocComponent(Docu DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`); } if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { - Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document); + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, "layoutCustom"); e.stopPropagation(); } if (de.data instanceof DragManager.LinkDragData) { @@ -383,12 +378,11 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action freezeNativeDimensions = (): void => { - let proto = this.Document.isTemplateDoc ? this.props.Document : Doc.GetProto(this.props.Document); - proto.autoHeight = this.Document.autoHeight = false; - proto.ignoreAspect = !proto.ignoreAspect; - if (!proto.ignoreAspect && !proto.nativeWidth) { - proto.nativeWidth = this.props.PanelWidth(); - proto.nativeHeight = this.props.PanelHeight(); + this.layoutDoc.autoHeight = this.layoutDoc.autoHeight = false; + this.layoutDoc.ignoreAspect = !this.layoutDoc.ignoreAspect; + if (!this.layoutDoc.ignoreAspect && !this.layoutDoc.nativeWidth) { + this.layoutDoc.nativeWidth = this.props.PanelWidth(); + this.layoutDoc.nativeHeight = this.props.PanelHeight(); } } @@ -505,7 +499,7 @@ export class DocumentView extends DocComponent(Docu layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) { layoutItems.push({ description: "Use Custom Layout", event: () => DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); - } else if (this.props.Document.layoutNative) { + } else { layoutItems.push({ description: "Use Native Layout", event: () => DocumentView.makeNativeViewClicked(this.props.Document), icon: "concierge-bell" }); } !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); @@ -599,7 +593,7 @@ export class DocumentView extends DocComponent(Docu return (showTitle ? 25 : 0) + 1; } - childScaling = () => (this.props.Document.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); @computed get contents() { return ((Docu this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) : ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); - const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + const nativeWidth = this.layoutDoc.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.layoutDoc.ignoreAspect ? `${this.nativeWidth}px` : "100%"; + const nativeHeight = this.layoutDoc.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); @@ -686,7 +680,7 @@ export class DocumentView extends DocComponent(Docu const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid", "solid"]; - let highlighting = fullDegree && this.props.Document.type !== DocumentType.FONTICON && this.props.Document.viewType !== CollectionViewType.Linear; + let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear; return (
(Docu color: StrCast(this.Document.color), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, - background: this.props.Document.type === DocumentType.FONTICON || this.props.Document.viewType === CollectionViewType.Linear ? undefined : backgroundColor, + background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc.viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, - transform: `scale(${this.props.Document.fitWidth ? 1 : this.props.ContentScaling()})`, + transform: `scale(${this.layoutDoc.fitWidth ? 1 : this.props.ContentScaling()})`, opacity: this.Document.opacity }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} @@ -707,7 +701,7 @@ export class DocumentView extends DocComponent(Docu > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => //this.linkEndpointDoc(d).type === DocumentType.PDFANNO ? (null) : -
+
)} {!showTitle && !showCaption ? @@ -733,33 +727,6 @@ export class DocumentView extends DocComponent(Docu } } -export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField: string, oldLayout?: Doc) { - let oldLayoutExt = oldLayout || await Cast(doc[oldLayoutField], Doc); - if (oldLayoutExt) { - oldLayoutExt.autoHeight = doc.autoHeight; - oldLayoutExt.width = doc.width; - oldLayoutExt.height = doc.height; - oldLayoutExt.nativeWidth = doc.nativeWidth; - oldLayoutExt.nativeHeight = doc.nativeHeight; - oldLayoutExt.ignoreAspect = doc.ignoreAspect; - oldLayoutExt.type = doc.type; - oldLayoutExt.layout = doc.layout; - } - - let newLayoutExt = newLayoutField && await Cast(doc[newLayoutField], Doc); - if (newLayoutExt) { - doc.autoHeight = newLayoutExt.autoHeight; - doc.width = newLayoutExt.width; - doc.height = newLayoutExt.height; - doc.nativeWidth = newLayoutExt.nativeWidth; - doc.nativeHeight = newLayoutExt.nativeHeight; - doc.ignoreAspect = newLayoutExt.ignoreAspect; - doc.type = newLayoutExt.type; - doc.layout = await newLayoutExt.layout; - } -} - Scripting.addGlobal(function toggleDetail(doc: any) { - let native = typeof doc.layout === "string"; - swapViews(doc, native ? "layoutCustom" : "layoutNative", native ? "layoutNative" : "layoutCustom"); + doc.layoutKey = StrCast(doc.layoutKey, "layout") === "layout" ? "layoutCustom" : "layout"; }); \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ac10d729a..f866e6f7e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -271,7 +271,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe newLayout = Doc.MakeDelegate(draggedDoc); newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`); } - this.props.Document.layout = newLayout; + this.props.Document.layoutCustom = newLayout; + this.props.Document.layoutKey = "layoutCustom"; e.stopPropagation(); // embed document when dragging with a userDropAction or an embedDoc flag set } else if (de.data.userDropAction || de.data.embedDoc) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2fc4c04e6..dc80af268 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -2,7 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye } from '@fortawesome/free-regular-svg-icons'; import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -13,7 +13,7 @@ import { ComputedField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; import { RouteStore } from '../../../server/RouteStore'; -import { Utils, returnOne, emptyFunction } from '../../../Utils'; +import { Utils, returnOne, emptyFunction, OmitKeys } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; @@ -159,7 +159,7 @@ export class ImageBox extends DocAnnotatableComponent { - let proto = Doc.GetProto(this.props.Document); - let nw = this.props.Document.nativeWidth; - let nh = this.props.Document.nativeHeight; - let w = this.props.Document.width; - let h = this.props.Document.height; - proto.rotation = (NumCast(this.props.Document.rotation) + 90) % 360; - proto.nativeWidth = nh; - proto.nativeHeight = nw; - this.props.Document.width = h; - this.props.Document.height = w; + let nw = this.layoutDoc.nativeWidth; + let nh = this.layoutDoc.nativeHeight; + let w = this.layoutDoc.width; + let h = this.layoutDoc.height; + this.layoutDoc.rotation = (NumCast(this.layoutDoc.rotation) + 90) % 360; + this.layoutDoc.nativeWidth = nh; + this.layoutDoc.nativeHeight = nw; + this.layoutDoc.width = h; + this.layoutDoc.height = w; }); specificContextMenu = (e: React.MouseEvent): void => { @@ -251,7 +251,7 @@ export class ImageBox extends DocAnnotatableComponent @@ -379,16 +379,17 @@ export class ImageBox extends DocAnnotatableComponent 20) { @@ -401,14 +402,14 @@ export class ImageBox extends DocAnnotatableComponent - {() => [this.content]} + {() => [this.content(this.layoutDoc)]}
); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index f31128356..f3ea8328b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import 'react-image-lightbox/style.css'; -import { Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt, WidthSym, Doc } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; import { Cast } from "../../../new_fields/Types"; @@ -65,11 +65,13 @@ export class PDFBox extends DocAnnotatableComponent }, { fireImmediately: true }); } + get layoutDoc() { return Doc.Layout(this.props.Document); } + loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; - this.Document.nativeWidth = nw * 96 / 72; - this.Document.nativeHeight = nh * 96 / 72; - !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); + this.layoutDoc.nativeWidth = nw * 96 / 72; + this.layoutDoc.nativeHeight = nh * 96 / 72; + !this.layoutDoc.fitWidth && !this.layoutDoc.ignoreAspect && (this.layoutDoc.height = this.layoutDoc[WidthSym]() * (nh / nw)); } public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 19968e6e1..4c01279ae 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -55,14 +55,15 @@ export class VideoBox extends DocAnnotatableComponent { let aspect = this.player!.videoWidth / this.player!.videoHeight; - var nativeWidth = FieldValue(this.Document.nativeWidth, 0); - var nativeHeight = FieldValue(this.Document.nativeHeight, 0); + var nativeWidth = NumCast(this.layoutDoc.nativeWidth, 0); + var nativeHeight = NumCast(this.layoutDoc.nativeHeight, 0); if (!nativeWidth || !nativeHeight) { - if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth; - this.Document.nativeHeight = this.Document.nativeWidth / aspect; - this.Document.height = FieldValue(this.Document.width, 0) / aspect; + if (!this.layoutDoc.nativeWidth) this.layoutDoc.nativeWidth = this.player!.videoWidth; + this.layoutDoc.nativeHeight = NumCast(this.layoutDoc.nativeWidth) / aspect; + this.layoutDoc.height = NumCast(this.layoutDoc.width, 0) / aspect; } if (!this.Document.duration) this.Document.duration = this.player!.duration; } @@ -107,7 +108,7 @@ export class VideoBox extends DocAnnotatableComponent {"" + Math.round(curTime)} {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} @@ -312,7 +313,7 @@ export class VideoBox extends DocAnnotatableComponent { this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY); - this.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); + this.Seek(Math.max(0, NumCast(this.layoutDoc.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); e.stopImmediatePropagation(); } @action @@ -320,10 +321,10 @@ export class VideoBox extends DocAnnotatableComponent Math.round(this.Document.currentTimecode || 0)); return