aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts30
-rw-r--r--src/client/util/DocumentManager.ts4
-rw-r--r--src/client/views/DocumentDecorations.tsx8
-rw-r--r--src/client/views/MainView.tsx7
-rw-r--r--src/client/views/collections/CollectionDockingView.scss5
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx63
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx51
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx3
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx6
-rw-r--r--src/client/views/nodes/DocumentView.tsx9
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx88
-rw-r--r--src/client/views/nodes/ImageBox.tsx1
-rw-r--r--src/client/views/nodes/PDFBox.tsx20
-rw-r--r--src/client/views/pdf/Annotation.tsx18
-rw-r--r--src/client/views/pdf/PDFViewer.tsx45
-rw-r--r--src/new_fields/Doc.ts2
18 files changed, 203 insertions, 163 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 9e03aad78..4b892aa70 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -307,4 +307,34 @@ export namespace JSONUtils {
return results;
}
+}
+
+const easeInOutQuad = (currentTime: number, start: number, change: number, duration: number) => {
+ let newCurrentTime = currentTime / (duration / 2);
+
+ if (newCurrentTime < 1) {
+ return (change / 2) * newCurrentTime * newCurrentTime + start;
+ }
+
+ newCurrentTime -= 1;
+ return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
+};
+
+export default function smoothScroll(duration: number, element: HTMLElement, to: number) {
+ const start = element.scrollTop;
+ const change = to - start;
+ const startDate = new Date().getTime();
+
+ const animateScroll = () => {
+ const currentDate = new Date().getTime();
+ const currentTime = currentDate - startDate;
+ element.scrollTop = easeInOutQuad(currentTime, start, change, duration);
+
+ if (currentTime < duration) {
+ requestAnimationFrame(animateScroll);
+ } else {
+ element.scrollTop = to;
+ }
+ };
+ animateScroll();
} \ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e60ab09bb..c048125c5 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -132,7 +132,7 @@ export class DocumentManager {
let doc = Doc.GetProto(docDelegate);
const contextDoc = await Cast(doc.annotationOn, Doc);
if (contextDoc) {
- contextDoc.panY = doc.y;
+ contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2;
}
let docView: DocumentView | null;
@@ -178,7 +178,7 @@ export class DocumentManager {
(dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined);
setTimeout(() => {
this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
- }, 10);
+ }, 1000);
}
}
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index dfb0b89f3..9d42eb719 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -516,8 +516,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
doc.x = (doc.x || 0) + dX * (actualdW - width);
doc.y = (doc.y || 0) + dY * (actualdH - height);
let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here...
- let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight);
- if (fixedAspect && e.ctrlKey && BoolCast(doc.ignoreAspect)) {
+ let fixedAspect = e.ctrlKey || (!doc.ignoreAspect && nwidth && nheight);
+ if (fixedAspect && e.ctrlKey && doc.ignoreAspect) {
doc.ignoreAspect = false;
proto.nativeWidth = nwidth = doc.width || 0;
proto.nativeHeight = nheight = doc.height || 0;
@@ -532,7 +532,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true);
}
doc.width = actualdW;
- if (fixedAspect) doc.height = nheight / nwidth * doc.width;
+ if (fixedAspect && !doc.fitWidth) doc.height = nheight / nwidth * doc.width;
else doc.height = actualdH;
}
else {
@@ -540,7 +540,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true);
}
doc.height = actualdH;
- if (fixedAspect) doc.width = nwidth / nheight * doc.height;
+ if (fixedAspect && !doc.fitWidth) doc.width = nwidth / nheight * doc.height;
else doc.width = actualdW;
}
} else {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 332e07f1c..2b5a2698e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { 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 } 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 } 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';
@@ -594,6 +594,7 @@ export class MainView extends React.Component {
<li key="marker"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button></li>
<li key="eraser"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button></li>
<li key="inkControls"><InkingControl /></li>
+ <li key="logout"><button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button></li>
</ul>
</div>
</div >;
@@ -609,12 +610,8 @@ export class MainView extends React.Component {
/* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
@computed
get miscButtons() {
- let logoutRef = React.createRef<HTMLDivElement>();
- const prompt = CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out";
return [
this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
- <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
- <button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{prompt}</button></div>
];
}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 0e7e0afa7..6f5abd05b 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,8 +1,5 @@
@import "../../views/globalCssVariables.scss";
-.collectiondockingview-content {
- height: 100%;
-}
.lm_active .messageCounter{
color:white;
background: #999999;
@@ -21,7 +18,7 @@
.collectiondockingview-container {
width: 100%;
- height: 100%;
+ height:100%;
border-style: solid;
border-width: $COLLECTION_BORDER_WIDTH;
position: absolute;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6d054b4fc..963851a12 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -32,6 +32,7 @@ import React = require("react");
import { ButtonSelector } from './ParentDocumentSelector';
import { DocumentType } from '../../documents/DocumentTypes';
library.add(faFile);
+const _global = (window /* browser */ || global /* node */) as any;
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -541,12 +542,11 @@ interface DockedFrameProps {
}
@observer
export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
- _mainCont: HTMLDivElement | undefined = undefined;
+ _mainCont: HTMLDivElement | null = null;
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
@observable private _dataDoc: Opt<Doc>;
-
@observable private _isActive: boolean = false;
get _stack(): any {
@@ -584,6 +584,13 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
componentDidMount() {
+ let observer = new _global.ResizeObserver(action((entries: any) => {
+ for (let entry of entries) {
+ this._panelWidth = entry.contentRect.width;
+ this._panelHeight = entry.contentRect.height;
+ }
+ }));
+ observer.observe(this.props.glContainer._element[0]);
this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
this.props.glContainer.on("tab", this.onActiveContentItemChanged);
this.onActiveContentItemChanged();
@@ -602,15 +609,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
}
- panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
- panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight()));
+ 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()));
- nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
- nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
+ 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;
contentScaling = () => {
if (this._document!.type === DocumentType.PDF) {
- if (this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) {
+ 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);
} else {
return this._panelHeight / NumCast(this._document!.nativeHeight);
@@ -646,13 +654,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
}
}
- @computed get docView() {
- if (!this._document) {
- return (null);
- }
- let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc;
- return <DocumentView key={this._document[Id]}
- Document={this._document}
+ docView(document: Doc) {
+ let resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
+ return <DocumentView key={document[Id]}
+ Document={document}
DataDoc={resolvedDataDoc}
bringToFront={emptyFunction}
addDocument={undefined}
@@ -675,28 +680,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
getScale={returnOne} />;
}
- @computed get content() {
- return (
- <div className="collectionDockingView-content" ref={action((ref: HTMLDivElement) => {
- this._mainCont = ref;
- if (ref) {
- this._panelWidth = Number(getComputedStyle(ref).width!.replace("px", ""));
- this._panelHeight = Number(getComputedStyle(ref).height!.replace("px", ""));
- }
- })}
- style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
- {this.docView}
- </div >);
- }
-
render() {
- if (!this._isActive || !this._document) return null;
- let theContent = this.content;
- return !this._document ? (null) :
- <Measure offset onResize={action((r: any) => { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}>
- {({ measureRef }) => <div ref={measureRef}>
- {theContent}
- </div>}
- </Measure>;
+ return (!this._isActive || !this._document) ? (null) :
+ (<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
+ style={{
+ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
+ height: this._document && this._document.fitWidth ? undefined : "100%"
+ }}>
+ {this.docView(this._document)}
+ </div >);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 597f3f745..45de0fefa 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -160,13 +160,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (!d) return 0;
let nw = NumCast(d.nativeWidth);
let nh = NumCast(d.nativeHeight);
- if (!d.ignoreAspect && nw && nh) {
+ if (!d.ignoreAspect && !d.fitWidth && nw && nh) {
let aspect = nw && nh ? nh / nw : 1;
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
return wid * aspect;
}
- return d[HeightSym]();
+ return d.fitWidth ? Math.min(this.props.PanelHeight() - 2 * this.yMargin, d[HeightSym]()) : d[HeightSym]();
}
columnDividerDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 07bb87c70..a9699d17b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -401,27 +401,34 @@ 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 newState = HistoryUtil.getState();
- newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
- HistoryUtil.pushState(newState);
-
- let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
-
- this.setPan(newPanX, newPanY);
- this.Document.panTransformType = "Ease";
- this.props.focus(this.props.Document);
- willZoom && this.setScaleToZoom(doc, scale);
-
- afterFocus && setTimeout(() => {
- if (afterFocus && afterFocus()) {
- this.Document.panX = savedState.px;
- this.Document.panY = savedState.py;
- this.Document.scale = savedState.s;
- this.Document.panTransformType = savedState.pt;
- }
- }, 1000);
+ if (this.props.Document.scrollHeight) {
+ let annotOn = Cast(doc.annotationOn, Doc) as Doc;
+ let offset = annotOn && (NumCast(annotOn.height) / 2);
+ 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;
+ const newState = HistoryUtil.getState();
+ newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
+ HistoryUtil.pushState(newState);
+
+ let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
+
+ this.setPan(newPanX, newPanY);
+ this.Document.panTransformType = "Ease";
+ this.props.focus(this.props.Document);
+ willZoom && this.setScaleToZoom(doc, scale);
+
+ afterFocus && setTimeout(() => {
+ if (afterFocus && afterFocus()) {
+ this.Document.panX = savedState.px;
+ this.Document.panY = savedState.py;
+ this.Document.scale = savedState.s;
+ this.Document.panTransformType = savedState.pt;
+ }
+ }, 1000);
+ }
+
}
setScaleToZoom = (doc: Doc, scale: number = 0.5) => {
@@ -449,7 +456,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
PanelHeight: childLayout[HeightSym],
ContentScaling: returnOne,
ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.ContainingCollectionDoc,
+ ContainingCollectionDoc: this.props.Document,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
parentActive: this.props.active,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 44611869e..82193aefa 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -188,16 +188,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerUp = (e: PointerEvent): void => {
if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
- // console.log("pointer up!");
if (this._visible) {
- // console.log("visible");
let mselect = this.marqueeSelect();
if (!e.shiftKey) {
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
}
this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
}
- //console.log("invisible");
this.cleanupInteractions(true);
if (e.altKey) {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c3d2c9e51..c4fed200f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -77,7 +77,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
borderRounding = () => {
let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
- let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout as Doc : undefined;
+ let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout : undefined;
let br = StrCast((ld || this.props.Document).borderRounding);
br = !br && ruleRounding ? ruleRounding : br;
if (br.endsWith("%")) {
@@ -100,7 +100,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@observable _animPos: number[] | undefined = undefined;
- finalPanelWidh = () => this.dataProvider ? this.dataProvider.width : this.panelWidth();
+ finalPanelWidth = () => this.dataProvider ? this.dataProvider.width : this.panelWidth();
finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight();
render() {
@@ -124,7 +124,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
backgroundColor={this.clusterColorFunc}
- PanelWidth={this.finalPanelWidh}
+ PanelWidth={this.finalPanelWidth}
PanelHeight={this.finalPanelHeight}
/>
</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4986daa5e..779e701ea 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -226,15 +226,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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]];
- let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[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.some(l => l instanceof Promise)) {
+ 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,
@@ -608,7 +611,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "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 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");
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ec1b03a40..b93c78cfd 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -95,7 +95,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
- return <p><b>{field.title}</b></p>;
+ return <p><b>{field.title && field.title.toString()}</b></p>;
//return <p><b>{field.title + " : id= " + field[Id]}</b></p>;
// let returnHundred = () => 100;
// return (
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 007093826..db5814e7c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -82,7 +82,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _nodeClicked: any;
private _undoTyping?: UndoManager.Batch;
private _searchReactionDisposer?: Lambda;
- private _scroolToRegionReactionDisposer: Opt<IReactionDisposer>;
+ private _scrollToRegionReactionDisposer: Opt<IReactionDisposer>;
private _reactionDisposer: Opt<IReactionDisposer>;
private _textReactionDisposer: Opt<IReactionDisposer>;
private _heightReactionDisposer: Opt<IReactionDisposer>;
@@ -142,7 +142,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
}
FormattedTextBox.Instance = this;
- this._scroolToRegionReactionDisposer = reaction(
+ this._scrollToRegionReactionDisposer = reaction(
() => StrCast(this.props.Document.scrollToLinkID),
async (scrollToLinkID) => {
let findLinkFrag = (frag: Fragment, editor: EditorView) => {
@@ -167,7 +167,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
};
let start = -1;
-
if (this._editorView && scrollToLinkID) {
let editor = this._editorView;
let ret = findLinkFrag(editor.state.doc.content, editor);
@@ -181,12 +180,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
setTimeout(() => this.unhighlightSearchTerms(), 2000);
-
- this.props.Document.scrollToLinkID = undefined;
}
+ this.props.Document.scrollToLinkID = undefined;
}
- }
+ },
+ { fireImmediately: true }
);
}
@@ -793,7 +792,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentWillUnmount() {
- this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer();
+ this._scrollToRegionReactionDisposer && this._scrollToRegionReactionDisposer();
this._rulesReactionDisposer && this._rulesReactionDisposer();
this._reactionDisposer && this._reactionDisposer();
this._proxyReactionDisposer && this._proxyReactionDisposer();
@@ -816,6 +815,40 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
e.stopPropagation();
}
let ctrlKey = e.ctrlKey;
+ if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
+ e.preventDefault();
+ }
+ }
+
+ onPointerUp = (e: React.PointerEvent): void => {
+ FormattedTextBoxComment.textBox = this;
+ if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+ e.stopPropagation();
+ }
+ }
+
+ @action
+ onFocused = (e: React.FocusEvent): void => {
+ document.removeEventListener("keypress", this.recordKeyHandler);
+ document.addEventListener("keypress", this.recordKeyHandler);
+ this.tryUpdateHeight();
+ if (!this.props.isOverlay) {
+ FormattedTextBox.InputBoxOverlay = this;
+ } else {
+ if (this._ref.current) {
+ this._ref.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll;
+ }
+ }
+ }
+ onPointerWheel = (e: React.WheelEvent): void => {
+ // if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time
+ if (this.props.isSelected() || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+ e.stopPropagation();
+ }
+ }
+
+ onClick = (e: React.MouseEvent): void => {
+ let ctrlKey = e.ctrlKey;
if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) {
let href = (e.target as any).href;
let location: string;
@@ -829,8 +862,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos);
if (node) {
let link = node.marks.find(m => m.type === this._editorView!.state.schema.marks.link);
- href = link && link.attrs.href;
- location = link && link.attrs.location;
+ if (link && !(link.attrs.docref && link.attrs.title)) { // bcz: getting hacky. this indicates that we clicked on a PDF excerpt quotation. In this case, we don't want to follow the link (we follow only the actual hyperlink for the quotation which is handled above).
+ href = link && link.attrs.href;
+ location = link && link.attrs.location;
+ }
}
if (href) {
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
@@ -848,7 +883,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return;
}
}
- if (targetContext) {
+ if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) {
DocumentManager.Instance.jumpToDocument(targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
} else if (jumpToDoc) {
DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
@@ -870,39 +905,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
- if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
- e.preventDefault();
- }
- }
-
- onPointerUp = (e: React.PointerEvent): void => {
- FormattedTextBoxComment.textBox = this;
- if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
- e.stopPropagation();
- }
- }
-
- @action
- onFocused = (e: React.FocusEvent): void => {
- document.removeEventListener("keypress", this.recordKeyHandler);
- document.addEventListener("keypress", this.recordKeyHandler);
- this.tryUpdateHeight();
- if (!this.props.isOverlay) {
- FormattedTextBox.InputBoxOverlay = this;
- } else {
- if (this._ref.current) {
- this._ref.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll;
- }
- }
- }
- onPointerWheel = (e: React.WheelEvent): void => {
- // if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time
- if (this.props.isSelected() || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
- e.stopPropagation();
- }
- }
-
- onClick = (e: React.MouseEvent): void => {
// this hackiness handles clicking on the list item bullets to do expand/collapse. the bullets are ::before pseudo elements so there's no real way to hit test against them.
if (this.props.isSelected() && e.nativeEvent.offsetX < 40) {
let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 1645c4ffd..9e9fe1324 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -38,6 +38,7 @@ library.add(faFileAudio, faAsterisk);
export const pageSchema = createSchema({
curPage: "number",
+ fitWidth: "boolean"
});
interface Window {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 0fcbaaa7c..fe71e76fd 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -20,6 +20,9 @@ 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);
@@ -58,7 +61,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
this.Document.nativeWidth = nw * 96 / 72;
this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72;
}
- this.Document.height = this.Document[WidthSym]() * (nh / nw);
+ !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw));
}
public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); }
@@ -165,14 +168,23 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
</div>);
}
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
+ let funcs: ContextMenuProps[] = [];
+ pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
+ funcs.push({ description: "Toggle Fit Width " + (this.Document.fitWidth ? "Off" : "On"), event: () => this.Document.fitWidth = !this.Document.fitWidth, icon: "expand-arrows-alt" });
+
+ 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 ?
<div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> :
- <div className={classname} onPointerDown={(e: React.PointerEvent) => {
+ <div className={classname} onContextMenu={this.specificContextMenu} onPointerDown={(e: React.PointerEvent) => {
let hit = document.elementFromPoint(e.clientX, e.clientY);
- if (hit && hit.localName === "span" && this.props.isSelected()) {
+ if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation
e.button === 0 && e.stopPropagation();
}
}}>
@@ -182,7 +194,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
addDocTab={this.props.addDocTab} GoToPage={this.gotoPage}
pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} />
{this.settingsPanel()}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index a9fa883c8..3ed85f6a5 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -85,7 +85,15 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
@action
onPointerDown = async (e: React.PointerEvent) => {
- if (e.button === 0) {
+ if (e.button === 2 || e.ctrlKey) {
+ PDFMenu.Instance.Status = "annotation";
+ PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ PDFMenu.Instance.Pinned = false;
+ PDFMenu.Instance.AddTag = this.addTag.bind(this);
+ PDFMenu.Instance.PinToPres = this.pinToPres;
+ 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);
@@ -96,14 +104,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
}
}
- if (e.button === 2) {
- PDFMenu.Instance.Status = "annotation";
- PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- PDFMenu.Instance.Pinned = false;
- PDFMenu.Instance.AddTag = this.addTag.bind(this);
- PDFMenu.Instance.PinToPres = this.pinToPres;
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
- }
}
addTag = (key: string, value: string): boolean => {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5ad4ffd48..13fd8ea98 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,14 +9,12 @@ 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 { emptyFunction, returnOne, Utils } from "../../../Utils";
+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";
import { Transform } from "../../util/Transform";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import Annotation from "./Annotation";
import PDFMenu from "./PDFMenu";
import "./PDFViewer.scss";
import React = require("react");
@@ -24,7 +22,8 @@ import * as rp from "request-promise";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
import { CollectionView } from "../collections/CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
+import Annotation from "./Annotation";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
@@ -41,6 +40,7 @@ interface IViewerProps {
PanelWidth: () => number;
PanelHeight: () => number;
ContentScaling: () => number;
+ select: (isCtrlPressed: boolean) => void;
renderDepth: number;
isSelected: () => boolean;
loaded: (nw: number, nh: number, np: number) => void;
@@ -106,11 +106,20 @@ export class PDFViewer extends React.Component<IViewerProps> {
// 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 && this._selectionReactionDisposer();
- this._selectionReactionDisposer = undefined;
- })
+ this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.setupPdfJsViewer());
+ this._reactionDisposer = reaction(
+ () => this.props.Document.scrollY,
+ (scrollY) => {
+ if (scrollY !== undefined) {
+ if (this._showCover || this._showWaiting) {
+ this.setupPdfJsViewer();
+ }
+ this._mainCont.current && smoothScroll(1000, this._mainCont.current, NumCast(this.props.Document.scrollY) || 0);
+ this.props.Document.scrollY = undefined;
+ }
+ },
+ { fireImmediately: true }
+ );
}
componentWillUnmount = () => {
@@ -153,12 +162,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
i === this.props.pdf.numPages - 1 && this.props.loaded((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]),
(page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]), i);
}))));
- Doc.GetProto(this.props.Document).scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0);
+ Doc.GetProto(this.props.Document).scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72;
}
}
@action
setupPdfJsViewer = async () => {
+ this._selectionReactionDisposer && this._selectionReactionDisposer();
+ this._selectionReactionDisposer = undefined;
this._showWaiting = true;
this.props.setPdfViewer(this);
await this.initialLoad();
@@ -180,10 +191,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
}),
{ fireImmediately: true }
);
- this._reactionDisposer = reaction(
- () => this.props.Document.panY,
- () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.props.Document.panY) || 0, behavior: "auto" })
- );
document.removeEventListener("copy", this.copy);
document.addEventListener("copy", this.copy);
@@ -228,10 +235,9 @@ export class PDFViewer extends React.Component<IViewerProps> {
annoDocs.push(annoDoc);
annoDoc.isButton = true;
anno.remove();
- // this.props.addDocument && this.props.addDocument(annoDoc, false);
mainAnnoDoc = annoDoc;
+ mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
mainAnnoDocProto.y = annoDoc.y;
- mainAnnoDocProto = Doc.GetProto(annoDoc);
} else {
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
let annoDoc = new Doc();
@@ -381,7 +387,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
this._downX = e.clientX;
this._downY = e.clientY;
if (NumCast(this.props.Document.scale, 1) !== 1) return;
- if (e.button !== 0 && this.active()) {
+ if ((e.button !== 0 || e.altKey) && this.active()) {
this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
}
this._marqueeing = false;
@@ -638,18 +644,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
@computed get annotationLayer() {
- trace();
return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
<Annotation {...this.props} anno={anno} key={`${anno[Id]}-annotation`} />)}
</div>;
}
@computed get pdfViewerDiv() {
- trace();
return <div className="pdfViewer-text" ref={this._viewer} style={{ transformOrigin: "left top" }} />;
}
@computed get standinViews() {
- trace();
return <>
{this._showCover ? this.getCoverImage() : (null)}
{this._showWaiting ? <img className="pdfViewer-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)}
@@ -710,7 +713,7 @@ class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> {
width: `${this.props.width()}px`, height: `${this.props.height()}px`,
border: `${this.props.width() === 0 ? "" : "2px dashed black"}`
}}>
- </div>
+ </div>;
}
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1b3c8b0b0..79b73aba8 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -344,7 +344,7 @@ export namespace Doc {
let list = Cast(target[key], listSpec(Doc));
if (list) {
if (allowDuplicates !== true) {
- let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
+ let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
if (pind !== -1) {
list.splice(pind, 1);
}