From b789ac4cc3fe013139825f811d7574e1d305c2cc Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 19 May 2019 20:30:50 -0400 Subject: Added link follow behavior --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fa44ec9f3..5bff08280 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -239,7 +239,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.addDocTab(document, maxLocation)); } } } -- cgit v1.2.3-70-g09d2 From 059faf25a0f8ba80e1d2853dcefa1a70df4cbb6a Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 17:10:21 -0400 Subject: fixed icons for pdfs. fixed activeItem issue for golden layout. --- src/client/goldenLayout.js | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 58 +++++++++++----------- 3 files changed, 31 insertions(+), 30 deletions(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index ab2bcefed..54c9c6068 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -4466,7 +4466,7 @@ } if (this.contentItems.length > 0) { - initialItem = this.contentItems[this.config.activeItemIndex || 0]; + initialItem = this.contentItems[Math.min(this.contentItems.length - 1, this.config.activeItemIndex || 0)]; if (!initialItem) { throw new Error('Configured activeItemIndex out of bounds'); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 5bff08280..fa2b09ba7 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -189,6 +189,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(PdfDocumen @computed private get thumbnailPage() { return NumCast(this.props.Document.thumbnailPage, -1); } componentDidMount() { - let wasSelected = false; + let wasSelected = this.props.isSelected(); this._reactionDisposer = reaction( - () => this.props.isSelected(), + () => [this.props.isSelected(), this.curPage], () => { - if (this.curPage > 0 && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { + if (this.curPage > 0 && !this.props.isTopMost && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { this.saveThumbnail(); } wasSelected = this._interactive = this.props.isSelected(); @@ -246,16 +245,19 @@ export class PDFBox extends DocComponent(PdfDocumen @action saveThumbnail = () => { + this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); this._renderAsSvg = false; setTimeout(() => { let nwidth = FieldValue(this.Document.nativeWidth, 0); let nheight = FieldValue(this.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) + htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) .then(action((dataUrl: string) => { - SearchBox.convertDataUri(dataUrl, this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { - this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); - this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); - this._renderAsSvg = true; + SearchBox.convertDataUri(dataUrl, "icon" + this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { + if (returnedFilename) { + let url = DocServer.prepend(returnedFilename); + this.props.Document.thumbnail = new ImageField(new URL(url)); + } + runInAction(() => this._renderAsSvg = true); }) })) .catch(function (error: any) { @@ -292,7 +294,6 @@ export class PDFBox extends DocComponent(PdfDocumen } @computed get pdfContent() { - trace(); let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); if (!pdfUrl) { return

No pdf url to render

; @@ -309,7 +310,7 @@ export class PDFBox extends DocComponent(PdfDocumen ; let xf = (this.Document.nativeHeight || 0) / this.renderHeight; return
- + {body}
; @@ -317,26 +318,25 @@ export class PDFBox extends DocComponent(PdfDocumen @computed get pdfRenderer() { - let proxy = this._loaded ? (null) : this.imageProxyRenderer; let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if ((!this._interactive && proxy) || !pdfUrl) { + let proxy = this.imageProxyRenderer; + if ((!this._interactive && proxy && (!this.props.ContainingCollectionView || !this.props.ContainingCollectionView.props.isTopMost)) || !pdfUrl) { return proxy; } return [ this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element), this._currAnno.map((element: any) => element), - this.pdfContent, - proxy + this.pdfContent ]; } @computed get imageProxyRenderer() { let thumbField = this.props.Document.thumbnail; - if (thumbField) { - let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : + if (thumbField && this._renderAsSvg) { + let path = // this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - return ; + return ; } return (null); } -- cgit v1.2.3-70-g09d2 From 060455feec89c94073636f612ccca54a0aaabb90 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 21:29:17 -0400 Subject: fixed a few things for Firefox - dragging images, typing spaces, scrool speed. and focusing proeprly with zoomBasis. --- .vscode/launch.json | 9 +++++++++ .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 +++------ src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 +++ src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/.vscode/launch.json b/.vscode/launch.json index d245be2ad..3b6549767 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,15 @@ "url": "http://localhost:1050/login", "webRoot": "${workspaceFolder}", }, + { + "type": "firefox", + "request": "launch", + "name": "Launch Firefox against localhost", + "sourceMaps": "client", + "reAttach": true, + "url": "http://localhost:1050/login", + "webRoot": "${workspaceFolder}", + }, { "type": "chrome", "request": "launch", diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ee6f4821f..3ec7184d2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return; } e.stopPropagation(); - const coefficient = 1000; + const coefficient = 100; if (e.ctrlKey) { let deltaScale = (1 - (e.deltaY / coefficient)); @@ -215,7 +215,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action setPan(panX: number, panY: number) { - this.panDisposer && clearTimeout(this.panDisposer); this.props.Document.panTransformType = "None"; var scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); @@ -243,7 +242,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } - panDisposer?: NodeJS.Timeout; focusDocument = (doc: Doc) => { const panX = this.Document.panX; const panY = this.Document.panY; @@ -265,15 +263,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } SelectionManager.DeselectAll(); - const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; - const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; + const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2; const newState = HistoryUtil.getState(); newState.initializers[id] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); this.setPan(newPanX, newPanY); this.props.Document.panTransformType = "Ease"; this.props.focus(this.props.Document); - this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fa2b09ba7..7c3180496 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -177,6 +177,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { document.removeEventListener("pointerup", this.onPointerUp); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d15813f9a..5afef221c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -366,7 +366,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe style={{ pointerEvents: interactive ? "all" : "none", }} - onKeyDown={this.onKeyPress} + // onKeyDown={this.onKeyPress} onKeyPress={this.onKeyPress} onFocus={this.onFocused} onClick={this.onClick} @@ -378,7 +378,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe // tfs: do we need this event handler onWheel={this.onPointerWheel} > -
+
); } -- cgit v1.2.3-70-g09d2 From 3abf1b817a5ed0e2bcacaa08f4a15a9193794a3a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 21 May 2019 08:22:24 -0400 Subject: Fixed performance problems --- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 6 +++--- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 97e610e13..31864f357 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Docs } from "../../../documents/Documents"; import { SelectionManager } from "../../../util/SelectionManager"; @@ -344,10 +344,10 @@ export class MarqueeView extends React.Component } render() { - let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); + let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0]; return
- {!this._visible ? null : this.marqueeDiv} + {this._visible ? this.marqueeDiv : null}
{this.props.children}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7c3180496..bf0c272e3 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, reaction } from "mobx"; +import { action, computed, IReactionDisposer, reaction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; @@ -211,7 +211,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Doc.GetProto(doc)) + let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc)); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); if (altKey) { @@ -286,7 +286,9 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Tue, 21 May 2019 09:34:10 -0400 Subject: Fixed speed again? --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 10 +++++----- src/client/views/nodes/DocumentContentsView.tsx | 5 +++-- src/client/views/nodes/ImageBox.tsx | 2 +- src/new_fields/Doc.ts | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index bf0c272e3..aaaa6a9c5 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -266,15 +266,15 @@ export class CollectionFreeFormDocumentView extends DocComponent 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1; const screenWidth = Math.min(50 * NumCast(this.props.Document.nativeWidth, 0), 1800); let fadeUp = .75 * screenWidth; let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth; - zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; + // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; return (
Date: Wed, 22 May 2019 19:51:49 -0400 Subject: A bunch of fixes/changes --- src/client/documents/Documents.ts | 2 + src/client/util/DocumentManager.ts | 6 ++- src/client/views/InkingCanvas.scss | 42 ++++++++++++-------- src/client/views/SearchBox.tsx | 10 +++-- .../views/collections/CollectionPDFView.scss | 39 ++++++++++-------- src/client/views/collections/CollectionSubView.tsx | 4 ++ .../views/collections/CollectionVideoView.tsx | 46 +++++++++++++++++++++- .../collections/collectionFreeForm/MarqueeView.tsx | 5 ++- .../views/nodes/CollectionFreeFormDocumentView.tsx | 9 ++++- src/client/views/nodes/DocumentView.tsx | 12 +++--- src/client/views/nodes/ImageBox.tsx | 6 ++- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 16 +++++++- src/client/views/nodes/VideoBox.tsx | 8 +++- 14 files changed, 153 insertions(+), 54 deletions(-) (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 323534b2b..ae190a989 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -77,7 +77,9 @@ export namespace DocUtils { linkDoc.proto!.linkTags = "Default"; linkDoc.proto!.linkedTo = target; + linkDoc.proto!.linkedToPage = target.curPage; linkDoc.proto!.linkedFrom = source; + linkDoc.proto!.linkedFromPage = source.curPage; let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); if (!linkedFrom) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index f113ff1d7..65c4b9e4b 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -115,11 +115,11 @@ export class DocumentManager { } @undoBatch - public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void): Promise => { + public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise => { let doc = Doc.GetProto(docDelegate); - const page = NumCast(doc.page, undefined); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { + const page = NumCast(doc.page, linkPage || 0); const curPage = NumCast(contextDoc.curPage, page); if (page !== curPage) contextDoc.curPage = page; } @@ -127,11 +127,13 @@ export class DocumentManager { // 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))) { docView.props.Document.libraryBrush = true; + if (linkPage !== undefined) docView.props.Document.curPage = linkPage; docView.props.focus(docView.props.Document); } else { if (!contextDoc) { const actualDoc = Doc.MakeAlias(docDelegate); actualDoc.libraryBrush = true; + if (linkPage !== undefined) actualDoc.curPage = linkPage; (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc); } else { let contextView: DocumentView | null; diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index 8bcaf41dd..d95398f17 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -1,40 +1,50 @@ @import "globalCssVariables"; .inkingCanvas { - opacity:0.99; + opacity: 0.99; + .jsx-parser { - position:absolute; - width:100%; - height:100%; + position: absolute; + width: 100%; + height: 100%; + z-index: -1; // allows annotations to appear on videos when screen is full-size & ... } } -.inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect { + +.inkingCanvas-paths-ink, +.inkingCanvas-paths-markers, +.inkingCanvas-noSelect, +.inkingCanvas-canSelect { position: absolute; top: 0; - left:0; + left: 0; width: 8192px; height: 8192px; - cursor:"crosshair"; + cursor: "crosshair"; pointer-events: auto; - + } -.inkingCanvas-canSelect, -.inkingCanvas-noSelect { - top:-50000px; - left:-50000px; + +.inkingCanvas-canSelect, +.inkingCanvas-noSelect { + top: -50000px; + left: -50000px; width: 100000px; height: 100000px; } -.inkingCanvas-noSelect { + +.inkingCanvas-noSelect { pointer-events: none; cursor: "arrow"; } -.inkingCanvas-paths-ink, .inkingCanvas-paths-markers { + +.inkingCanvas-paths-ink, +.inkingCanvas-paths-markers { pointer-events: none; z-index: 10000; // overlays ink on top of everything cursor: "arrow"; } + .inkingCanvas-paths-markers { mix-blend-mode: multiply; -} - +} \ No newline at end of file diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 911fc8d9c..fa9462acf 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -21,6 +21,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { SetupDrag } from '../util/DragManager'; import { Docs } from '../documents/Documents'; import { RouteStore } from '../../server/RouteStore'; +import { NumCast } from '../../new_fields/Types'; library.add(faSearch); library.add(faObjectGroup); @@ -143,18 +144,21 @@ export class SearchBox extends React.Component { }); let x = 0; let y = 0; + let maxy = 300; for (const doc of docs) { doc.x = x; doc.y = y; doc.width = 200; - doc.height = 200; + doc.height = 200 * NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); + maxy = Math.max(doc.height, maxy); x += 250; if (x > 1000) { + maxy = 300; x = 0; - y += 250; + y += maxy + 25; } } - return Docs.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, title: `Search Docs: "${this.searchString}"` }); + return Docs.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this.searchString}"` }); } // Useful queries: diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss index f6fb79582..50201bae8 100644 --- a/src/client/views/collections/CollectionPDFView.scss +++ b/src/client/views/collections/CollectionPDFView.scss @@ -1,49 +1,56 @@ .collectionPdfView-buttonTray { - top : 15px; - left : 20px; - position: relative; + top: 15px; + left: 20px; + position: relative; transform-origin: left top; position: absolute; } + .collectionPdfView-thumb { - width:25px; - height:25px; + width: 25px; + height: 25px; transform-origin: left top; position: absolute; background: darkgray; } + .collectionPdfView-slider { - width:25px; - height:25px; + width: 25px; + height: 25px; transform-origin: left top; position: absolute; background: lightgray; } -.collectionPdfView-cont{ + +.collectionPdfView-cont { width: 100%; height: 100%; - position: absolute; + position: absolute; top: 0; - left:0; + left: 0; + z-index: -1; } + .collectionPdfView-cont-dragging { span { user-select: none; } } + .collectionPdfView-backward { - color : white; + color: white; font-size: 24px; - top :0px; - left : 0px; + top: 0px; + left: 0px; position: absolute; background-color: rgba(50, 50, 50, 0.2); } + .collectionPdfView-forward { - color : white; + color: white; font-size: 24px; - top :0px; - left : 45px; + top: 0px; + left: 45px; position: absolute; background-color: rgba(50, 50, 50, 0.2); } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7800b35df..4a21e4465 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -153,6 +153,10 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action protected onDrop(e: React.DragEvent, options: DocumentOptions): void { + if (e.ctrlKey) { + e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl + return; + } let html = e.dataTransfer.getData("text/html"); let text = e.dataTransfer.getData("text/plain"); diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 27f23a1a8..7853544d5 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,4 +1,5 @@ import { action, observable, trace } from "mobx"; +import * as htmlToImage from "html-to-image"; import { observer } from "mobx-react"; import { ContextMenu } from "../ContextMenu"; import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView"; @@ -6,10 +7,14 @@ import React = require("react"); import "./CollectionVideoView.scss"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { emptyFunction } from "../../../Utils"; +import { emptyFunction, Utils } from "../../../Utils"; import { Id } from "../../../new_fields/FieldSymbols"; import { VideoBox } from "../nodes/VideoBox"; -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, Cast, StrCast } from "../../../new_fields/Types"; +import { VideoField } from "../../../new_fields/URLField"; +import { SearchBox } from "../SearchBox"; +import { DocServer } from "../../DocServer"; +import { Docs, DocUtils } from "../../documents/Documents"; @observer @@ -67,6 +72,43 @@ export class CollectionVideoView extends React.Component { 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 } + + let field = Cast(this.props.Document[this.props.fieldKey], VideoField); + if (field) { + let url = field.url.href; + ContextMenu.Instance.addItem({ + description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" + }); + } + let width = NumCast(this.props.Document.width); + let height = NumCast(this.props.Document.height); + ContextMenu.Instance.addItem({ + description: "Take Snapshot", event: async () => { + var canvas = document.createElement('canvas'); + canvas.width = 640; + canvas.height = 640 * NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.nativeWidth); + var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions + ctx && ctx.drawImage(this._videoBox!.player!, 0, 0, canvas.width, canvas.height); + + //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 = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, ""); + SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => { + if (returnedFilename) { + let url = DocServer.prepend(returnedFilename); + let imageSummary = Docs.ImageDocument(url, { + x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), + width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" + }); + this.props.addDocument && this.props.addDocument(imageSummary, false); + DocUtils.MakeLink(imageSummary, this.props.Document); + } + }); + }, + icon: "expand-arrows-alt" + }); } setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; }; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d72046800..973ebfbdd 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -296,7 +296,10 @@ export class MarqueeView extends React.Component SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { if (returnedFilename) { let url = DocServer.prepend(returnedFilename); - let imageSummary = Docs.ImageDocument(url, { x: bounds.left, y: bounds.top + 100 / zoomBasis, width: 150, title: "-summary image-" }); + let imageSummary = Docs.ImageDocument(url, { + x: bounds.left, y: bounds.top + 100 / zoomBasis, + width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" + }); summary.imageSummary = imageSummary; this.props.addDocument(imageSummary, false); } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index aaaa6a9c5..66d91f9c2 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -195,6 +195,7 @@ export class CollectionFreeFormDocumentView extends DocComponent l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab"); - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], altKey, document => this.props.addDocTab(document, maxLocation)); + DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]); } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 87c88f57c..01c4d82fb 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -234,12 +234,14 @@ export class DocumentView extends DocComponent(Docu makeButton = (e: React.MouseEvent): void => { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.isButton = !BoolCast(doc.isButton, false); - if (doc.isButton && !doc.nativeWidth) { - doc.nativeWidth = this.props.Document[WidthSym](); - doc.nativeHeight = this.props.Document[HeightSym](); - } else { + if (StrCast(doc.layout).indexOf("Formatted") !== -1) { // only need to freeze the dimensions of text boxes since they don't have a native width and height naturally + if (doc.isButton && !doc.nativeWidth) { + doc.nativeWidth = this.props.Document[WidthSym](); + doc.nativeHeight = this.props.Document[HeightSym](); + } else { - doc.nativeWidth = doc.nativeHeight = undefined; + doc.nativeWidth = doc.nativeHeight = undefined; + } } } fullScreenClicked = (e: React.MouseEvent): void => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 71bda6fea..cc0dc8220 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -47,7 +47,7 @@ export class ImageBox extends DocComponent(ImageD onLoad = (target: any) => { var h = this._imgRef.current!.naturalHeight; var w = this._imgRef.current!.naturalWidth; - if (this._photoIndex === 0 && (this.props as any).id !== "isExpander" && (!this.Document.nativeHeight || !this.Document.nativeWidth)) { + if (this._photoIndex === 0 && (this.props as any).id !== "isExpander" && (!this.Document.nativeWidth || !this.Document.nativeHeight || Math.abs(this.Document.nativeWidth / this.Document.nativeHeight - w / h) > 0.05)) { Doc.SetOnPrototype(this.Document, "nativeHeight", FieldValue(this.Document.nativeWidth, 0) * h / w); this.Document.height = FieldValue(this.Document.width, 0) * h / w; } @@ -165,8 +165,10 @@ export class ImageBox extends DocComponent(ImageD } choosePath(url: URL) { - if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) + const lower = url.href.toLowerCase(); + if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1 || !(lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) { return url.href; + } let ext = path.extname(url.href); return url.href.replace(ext, this._curSuffix + ext); } diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 8cb576786..849f17aa4 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -78,7 +78,7 @@ export class KeyValueBox extends React.Component { let rows: JSX.Element[] = []; let i = 0; - for (let key in ids) { + for (let key of Object.keys(ids).sort()) { rows.push(); } return rows; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5b8b15ed8..733bc920f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -23,6 +23,7 @@ import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; var path = require('path'); import React = require("react"); +import { ContextMenu } from "../ContextMenu"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -357,7 +358,7 @@ export class PDFBox extends DocComponent(PdfDocumen @computed get imageProxyRenderer() { let thumbField = this.props.Document.thumbnail; - if (thumbField && this._renderAsSvg) { + if (thumbField && this._renderAsSvg && NumCast(this.props.Document.thumbnailPage, 0) === this.Document.curPage) { // 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; @@ -380,10 +381,21 @@ export class PDFBox extends DocComponent(PdfDocumen } @action onKeyDown = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = true); @action onKeyUp = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = false); + onContextMenu = (e: React.MouseEvent): void => { + let field = Cast(this.Document[this.props.fieldKey], PdfField); + if (field) { + let url = field.url.href; + ContextMenu.Instance.addItem({ + description: "Copy path", event: () => { + Utils.CopyText(url); + }, icon: "expand-arrows-alt" + }); + } + } render() { let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return ( -
+
{this.pdfRenderer}
); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 6ae55d151..ab57b4b04 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -88,10 +88,14 @@ export class VideoBox extends DocComponent(VideoD if (vref) { vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.curPage, () => - vref.currentTime = NumCast(this.props.Document.curPage, 0), { fireImmediately: true }); + this._reactionDisposer = reaction(() => this.props.Document.curPage, () => { + if (!this.Playing) { + vref.currentTime = NumCast(this.props.Document.curPage, 0); + } + }, { fireImmediately: true }); } } + videoContent(path: string) { let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : ""); return