aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samuel_wilkins@brown.edu>2019-06-28 21:47:36 -0400
committerSam Wilkins <samuel_wilkins@brown.edu>2019-06-28 21:47:36 -0400
commitf9ec2ee53e6d19d02f0a6706470e05ed563d08bf (patch)
treec76208d5509cd3c8907bc6cc6fabef58080e9bed
parent79f301a2f74e88f1cf59064de320c199b5154827 (diff)
parent5a50af8589d9fb48d9395c124c6140b39c1a262b (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-rw-r--r--package.json2
-rw-r--r--src/client/views/DocumentDecorations.tsx50
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx14
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx38
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/client/views/nodes/FieldView.tsx7
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx8
-rw-r--r--src/client/views/nodes/ImageBox.tsx44
-rw-r--r--src/client/views/pdf/Annotation.tsx144
-rw-r--r--src/client/views/pdf/PDFViewer.tsx144
-rw-r--r--src/new_fields/Doc.ts21
13 files changed, 311 insertions, 195 deletions
diff --git a/package.json b/package.json
index dc829a045..51d1bab5d 100644
--- a/package.json
+++ b/package.json
@@ -196,4 +196,4 @@
"uuid": "^3.3.2",
"xoauth2": "^1.2.0"
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index db10007f4..61e9d209a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -26,6 +26,7 @@ import { Template, Templates } from "./Templates";
import React = require("react");
import { RichTextField } from '../../new_fields/RichTextField';
import { LinkManager } from '../util/LinkManager';
+import { ObjectField } from '../../new_fields/ObjectField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -77,32 +78,31 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._fieldKey = text.slice(1, text.length);
this._title = this.selectionTitle;
} else if (text.startsWith(">")) {
- let field = SelectionManager.SelectedDocuments()[0];
- let collection = field.props.ContainingCollectionView!.props.Document;
-
- let collectionKey = field.props.ContainingCollectionView!.props.fieldKey;
- let collectionKeyProp = `fieldKey={"${collectionKey}"}`;
+ let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
+ SelectionManager.DeselectAll();
+ let fieldTemplate = fieldTemplateView.props.Document;
+ let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
let metaKey = text.slice(1, text.length);
- let metaKeyProp = `fieldKey={"${metaKey}"}`;
-
- let template = Doc.MakeAlias(field.props.Document);
- template.proto = collection;
- template.title = metaKey;
- template.nativeWidth = Cast(field.nativeWidth, "number");
- template.nativeHeight = Cast(field.nativeHeight, "number");
- template.embed = true;
- template.isTemplate = true;
- template.templates = new List<string>([Templates.TitleBar(metaKey)]);
- if (field.props.Document.backgroundLayout) {
- let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
- let collectionAnoKeyProp = `fieldKey={"annotations"}`;
- template.layout = StrCast(field.props.Document.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
- template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
- } else {
- template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp);
+
+ // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+ let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
+ let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ if (backgroundLayout) {
+ layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
+ backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
}
- Doc.AddDocToList(collection, collectionKey, template);
- SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
+ let nw = Cast(fieldTemplate.nativeWidth, "number");
+ let nh = Cast(fieldTemplate.nativeHeight, "number");
+
+ fieldTemplate.title = metaKey;
+ fieldTemplate.layout = layout;
+ fieldTemplate.backgroundLayout = backgroundLayout;
+ fieldTemplate.nativeWidth = nw;
+ fieldTemplate.nativeHeight = nh;
+ fieldTemplate.embed = true;
+ fieldTemplate.isTemplate = true;
+ fieldTemplate.templates = new List<string>([Templates.TitleBar(metaKey)]);
+ fieldTemplate.proto = Doc.GetProto(docTemplate);
}
else {
if (SelectionManager.SelectedDocuments().length > 0) {
@@ -500,7 +500,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let nheight = doc.nativeHeight || 0;
let width = (doc.width || 0);
let height = (doc.height || (nheight / nwidth * width));
- let scale = element.props.ScreenToLocalTransform().Scale;
+ let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
let actualdW = Math.max(width + (dW * scale), 20);
let actualdH = Math.max(height + (dH * scale), 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 98bf513bb..5562676e9 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -349,12 +349,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
<div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
}
+
@computed
get previewPanel() {
+ // let layoutDoc = this.previewDocument;
+ // let resolvedDataDoc = (layoutDoc !== this.props.DataDoc) ? this.props.DataDoc : undefined;
+ // if (layoutDoc && !(Cast(layoutDoc.layout, Doc) instanceof Doc) &&
+ // resolvedDataDoc && resolvedDataDoc !== layoutDoc) {
+ // // ... so change the layout to be an expanded view of the template layout. This allows the view override the template's properties and be referenceable as its own document.
+ // layoutDoc = Doc.expandTemplateLayout(layoutDoc, resolvedDataDoc);
+ // }
+
+ let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
return <div ref={this.createTarget}>
<CollectionSchemaPreview
- Document={this.previewDocument}
- DataDocument={BoolCast(this.props.Document.isTemplate) ? this.previewDocument : this.props.DataDoc}
+ Document={layoutDoc}
+ DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
childDocs={this.childDocs}
renderDepth={this.props.renderDepth}
width={this.previewWidth}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6c4ea18a1..088a9bae8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,12 +4,13 @@ import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
-import { BoolCast, NumCast } from "../../../new_fields/Types";
+import { BoolCast, NumCast, Cast } from "../../../new_fields/Types";
import { emptyFunction, Utils } from "../../../Utils";
import { ContextMenu } from "../ContextMenu";
import { CollectionSchemaPreview } from "./CollectionSchemaView";
import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
+import { resolve } from "bluebird";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -66,17 +67,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
get singleColumnChildren() {
let children = this.childDocs.filter(d => !d.isMinimized);
return children.map((d, i) => {
+ let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]());
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!).scale(this.columnWidth / d[WidthSym]());
let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
- let height = () => this.singleColDocHeight(d);
+ let height = () => this.singleColDocHeight(layoutDoc);
return <div className="collectionStackingView-columnDoc"
key={d[Id]}
ref={dref}
style={{ width: width(), height: height() }} >
<CollectionSchemaPreview
- Document={d}
- DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc}
+ Document={layoutDoc}
+ DataDocument={d != this.props.DataDoc ? this.props.DataDoc : undefined}
renderDepth={this.props.renderDepth}
width={width}
height={height}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5c80fbd38..9e0130281 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -183,16 +183,20 @@ class TreeView extends React.Component<TreeViewProps> {
let keys = Array.from(Object.keys(this.resolvedDataDoc));
if (this.resolvedDataDoc.proto instanceof Doc) {
keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto)));
- while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
}
- let keyList: string[] = keys.reduce((l, key) => Cast(this.resolvedDataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]);
+ let keyList: string[] = keys.reduce((l, key) => {
+ let listspec = DocListCast(this.resolvedDataDoc[key]);
+ if (listspec && listspec.length)
+ return [...l, key];
+ return l;
+ }, [] as string[]);
keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key));
if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links");
if (keyList.indexOf(this.fieldKey) !== -1) {
keyList.splice(keyList.indexOf(this.fieldKey), 1);
}
keyList.splice(0, 0, this.fieldKey);
- return keyList;
+ return keyList.filter((item, index) => keyList.indexOf(item) >= index);
}
/**
* Renders the EditableView title element for placement into the tree.
@@ -233,7 +237,7 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // 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: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) });
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
@@ -322,14 +326,14 @@ class TreeView extends React.Component<TreeViewProps> {
this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
</ul >;
} else {
- console.log("PW = " + this.props.panelWidth());
- contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.props.panelHeight() }} key={this.props.document[Id]}>
+ let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
+ contentElement = <div ref={this._dref} style={{ display: "inline-block", height: layoutDoc[HeightSym]() }} key={this.props.document[Id]}>
<CollectionSchemaPreview
- Document={this.props.document}
+ Document={layoutDoc}
DataDocument={this.resolvedDataDoc}
renderDepth={this.props.renderDepth}
width={docWidth}
- height={this.props.panelHeight}
+ height={layoutDoc[HeightSym]}
getTransform={this.docTransform}
CollectionView={undefined}
addDocument={emptyFunction as any}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 15185ecb0..bbec33ba3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -28,6 +28,7 @@ import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
+
export const panZoomSchema = createSchema({
panX: "number",
panY: "number",
@@ -335,13 +336,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
- let datadoc = BoolCast(this.props.Document.isTemplate) || this.props.DataDoc === this.props.Document ? undefined : this.props.DataDoc;
- if (Cast(layoutDoc.layout, Doc) instanceof Doc) { // if this document is using a template to render, then set the dataDoc for the template to be this document
- datadoc = layoutDoc;
- }
+ getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
+ let resolvedDataDoc = this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+ let layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc);
return {
- DataDoc: datadoc,
+ DataDoc: resolvedDataDoc !== layoutDoc && resolvedDataDoc ? resolvedDataDoc : layoutDoc,
Document: layoutDoc,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
@@ -362,6 +361,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getScale: this.getScale
};
}
+ getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
+ return {
+ DataDoc: this.props.DataDoc,
+ Document: this.props.Document,
+ addDocument: this.props.addDocument,
+ removeDocument: this.props.removeDocument,
+ moveDocument: this.props.moveDocument,
+ ScreenToLocalTransform: this.getTransform,
+ renderDepth: this.props.renderDepth + 1,
+ selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
+ PanelWidth: layoutDoc[WidthSym],
+ PanelHeight: layoutDoc[HeightSym],
+ ContentScaling: returnOne,
+ ContainingCollectionView: this.props.CollectionView,
+ focus: this.focusDocument,
+ parentActive: this.props.active,
+ whenActiveChanged: this.props.whenActiveChanged,
+ bringToFront: this.bringToFront,
+ addDocTab: this.props.addDocTab,
+ zoomToScale: this.zoomToScale,
+ getScale: this.getScale
+ };
+ }
@computed.struct
get views() {
@@ -372,7 +394,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (Math.round(page) === Math.round(curPage) || page === -1) {
let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
- prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
+ prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getChildDocumentViewProps(doc)} />);
}
}
return prev;
@@ -416,7 +438,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
private childViews = () => [
- <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} DataDoc={this.props.DataDoc} />,
+ <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
...this.views
]
render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 473e79023..98f1ac0a3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -393,7 +393,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
- fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); };
+ fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.dataDoc, "onRight"); };
makeBtnClicked = (): void => {
let doc = Doc.GetProto(this.props.Document);
doc.isButton = !BoolCast(doc.isButton, false);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 55f61ddff..7c8509722 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -18,6 +18,8 @@ import { ImageBox } from "./ImageBox";
import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
import { Id } from "../../../new_fields/FieldSymbols";
+import { BoolCast, Cast } from "../../../new_fields/Types";
+import { DarpaDatasetDoc } from "../../northstar/model/idea/idea";
//
@@ -28,6 +30,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
export interface FieldViewProps {
fieldKey: string;
fieldExt: string;
+ leaveNativeSize?: boolean;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
@@ -72,7 +75,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <FormattedTextBox {...this.props} />;
}
else if (field instanceof ImageField) {
- return <ImageBox {...this.props} />;
+ return <ImageBox {...this.props} leaveNativeSize={true} />;
}
else if (field instanceof IconField) {
return <IconBox {...this.props} />;
@@ -86,7 +89,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
- return <p><b>{field.title + " + " + field[Id]}</b></p>;
+ return <p><b>{field.title + " : id= " + field[Id]}</b></p>;
// let returnHundred = () => 100;
// return (
// <DocumentContentsView Document={field}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2a45aeb43..ba6808737 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -211,7 +211,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
- field => this._editorView && !this._applyingChange &&
+ field => this._editorView && !this._applyingChange && this.props.Document[this.props.fieldKey] instanceof RichTextField &&
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
this.setupEditor(config, this.dataDoc, this.props.fieldKey);
@@ -247,6 +247,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.selectOnLoad) {
if (!this.props.isOverlay) this.props.select(false);
else this._editorView!.focus();
+ this.tryUpdateHeight();
}
}
@@ -382,6 +383,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
}
+ this.tryUpdateHeight();
+ }
+
+ @action
+ tryUpdateHeight() {
if (this.props.isOverlay && this.props.Document.autoHeight) {
let xf = this._ref.current!.getBoundingClientRect();
let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5c00d9d44..06bf65f73 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -20,6 +20,8 @@ import { positionSchema } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
+import { RouteStore } from '../../../server/RouteStore';
+var requestImageSize = require('../../util/request-image-size');
var path = require('path');
@@ -27,7 +29,7 @@ library.add(faImage);
export const pageSchema = createSchema({
- curPage: "number"
+ curPage: "number",
});
type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
@@ -41,7 +43,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
private _downX: number = 0;
private _downY: number = 0;
private _lastTap: number = 0;
- @observable private _photoIndex: number = 0;
@observable private _isOpen: boolean = false;
private dropDisposer?: DragManager.DragDropDisposer;
@@ -115,20 +116,21 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
e.stopPropagation();
}
+ @action
lightbox = (images: string[]) => {
if (this._isOpen) {
return (<Lightbox
- mainSrc={images[this._photoIndex]}
- nextSrc={images[(this._photoIndex + 1) % images.length]}
- prevSrc={images[(this._photoIndex + images.length - 1) % images.length]}
+ mainSrc={images[this.Document.curPage || 0]}
+ nextSrc={images[((this.Document.curPage || 0) + 1) % images.length]}
+ prevSrc={images[((this.Document.curPage || 0) + images.length - 1) % images.length]}
onCloseRequest={action(() =>
this._isOpen = false
)}
onMovePrevRequest={action(() =>
- this._photoIndex = (this._photoIndex + images.length - 1) % images.length
+ this.Document.curPage = ((this.Document.curPage || 0) + images.length - 1) % images.length
)}
onMoveNextRequest={action(() =>
- this._photoIndex = (this._photoIndex + 1) % images.length
+ this.Document.curPage = ((this.Document.curPage || 0) + 1) % images.length
)}
/>);
}
@@ -160,7 +162,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
@action
onDotDown(index: number) {
- this._photoIndex = index;
this.Document.curPage = index;
}
@@ -170,7 +171,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let left = (nativeWidth - paths.length * dist) / 2;
return paths.map((p, i) =>
<div className="imageBox-placer" key={i} >
- <div className="imageBox-dot" style={{ background: (i === this._photoIndex ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
+ <div className="imageBox-dot" style={{ background: (i === this.Document.curPage ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
</div>
);
}
@@ -199,6 +200,22 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
}
}
_curSuffix = "_m";
+
+ resize(srcpath: string, layoutdoc: Doc) {
+ requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath)
+ .then((size: any) => {
+ let aspect = size.height / size.width;
+ if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) {
+ setTimeout(action(() => {
+ layoutdoc.height = layoutdoc[WidthSym]() * aspect;
+ layoutdoc.nativeHeight = size.height;
+ layoutdoc.nativeWidth = size.width;
+ }), 0);
+ }
+ })
+ .catch((err: any) => console.log(err));
+ }
+
render() {
// 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;
@@ -212,6 +229,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
// this._curSuffix = "";
// if (w > 20) {
+ Doc.UpdateDocumentExtensionForField(this.extensionDoc, 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];
@@ -225,8 +243,10 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let rotation = NumCast(this.dataDoc.rotation, 0);
let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
- Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
- let srcpath = paths[Math.min(paths.length, this._photoIndex)];
+ let srcpath = paths[Math.min(paths.length, this.Document.curPage || 0)];
+
+ if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
+
return (
<div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
onPointerDown={this.onPointerDown}
@@ -235,7 +255,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
- // style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }}
+ // style={{ objectFit: (this.Document.curPage === 0 ? undefined : "contain") }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} />
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
new file mode 100644
index 000000000..74f4be51a
--- /dev/null
+++ b/src/client/views/pdf/Annotation.tsx
@@ -0,0 +1,144 @@
+import React = require("react");
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { AnnotationTypes, Viewer, scale } from "./PDFViewer";
+import { observer } from "mobx-react";
+import { observable, IReactionDisposer, reaction, action } from "mobx";
+import { BoolCast, NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import PDFMenu from "./PDFMenu";
+import { DocumentManager } from "../../util/DocumentManager";
+
+interface IAnnotationProps {
+ anno: Doc,
+ index: number,
+ parent: Viewer
+}
+
+export default class Annotation extends React.Component<IAnnotationProps> {
+ render() {
+ let annotationDocs = DocListCast(this.props.anno.annotations);
+ let res = annotationDocs.map(a => {
+ let type = NumCast(a.type);
+ switch (type) {
+ // case AnnotationTypes.Pin:
+ // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ case AnnotationTypes.Region:
+ return <RegionAnnotation parent={this.props.parent} document={a} index={this.props.index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ default:
+ return <div></div>;
+ }
+ });
+ return res;
+ }
+}
+
+interface IRegionAnnotationProps {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ index: number;
+ parent: Viewer;
+ document: Doc;
+}
+
+@observer
+class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
+ @observable private _backgroundColor: string = "red";
+
+ private _reactionDisposer?: IReactionDisposer;
+ private _scrollDisposer?: IReactionDisposer;
+ private _mainCont: React.RefObject<HTMLDivElement>;
+
+ constructor(props: IRegionAnnotationProps) {
+ super(props);
+
+ this._mainCont = React.createRef();
+ }
+
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => BoolCast(this.props.document.delete),
+ () => {
+ if (BoolCast(this.props.document.delete)) {
+ if (this._mainCont.current) {
+ this._mainCont.current.style.display = "none";
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
+
+ this._scrollDisposer = reaction(
+ () => this.props.parent.Index,
+ () => {
+ if (this.props.parent.Index === this.props.index) {
+ this.props.parent.scrollTo(this.props.y - 50);
+ }
+ }
+ );
+ }
+
+ componentWillUnmount() {
+ this._reactionDisposer && this._reactionDisposer();
+ this._scrollDisposer && this._scrollDisposer();
+ }
+
+ deleteAnnotation = () => {
+ let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group && annotation.indexOf(group) !== -1) {
+ let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+ this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+ }
+
+ if (group) {
+ let groupAnnotations = DocListCast(group.annotations);
+ groupAnnotations.forEach(anno => anno.delete = true);
+ }
+
+ PDFMenu.Instance.fadeOut(true);
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ if (e.button === 0) {
+ let targetDoc = Cast(this.props.document.target, Doc, null);
+ if (targetDoc) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, true);
+ }
+ }
+ 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.jumpTo(e.clientX, e.clientY, true);
+ }
+ }
+
+ addTag = (key: string, value: string): boolean => {
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group) {
+ let valNum = parseInt(value);
+ group[key] = isNaN(valNum) ? value : valNum;
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+ return (
+ <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
+ style={{
+ top: this.props.y * scale,
+ left: this.props.x * scale,
+ width: this.props.width * scale,
+ height: this.props.height * scale,
+ pointerEvents: "all",
+ backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+ }}></div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a440a1f27..9e9ddbd2d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,6 +23,7 @@ import { UndoManager } from "../../util/UndoManager";
import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
import { ScriptField } from "../../../new_fields/ScriptField";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import Annotation from "./Annotation";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
export const scale = 2;
@@ -69,7 +70,7 @@ interface IViewerProps {
* Handles rendering and virtualization of the pdf
*/
@observer
-class Viewer extends React.Component<IViewerProps> {
+export class Viewer extends React.Component<IViewerProps> {
// _visibleElements is the array of JSX elements that gets rendered
@observable.shallow private _visibleElements: JSX.Element[] = [];
// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
@@ -192,10 +193,14 @@ class Viewer extends React.Component<IViewerProps> {
let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
this._isPage = Array<string>(this.props.pdf.numPages);
// this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
+ const proms: Pdfjs.PDFPromise<any>[] = [];
for (let i = 0; i < this.props.pdf.numPages; i++) {
- await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
- // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
- let x = page.getViewport(scale);
+ proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+ pageSizes[i] = {
+ width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale,
+ height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale
+ };
+ // let x = page.getViewport(scale);
// page.getTextContent().then((text: Pdfjs.TextContent) => {
// // let tc = new Pdfjs.TextContentItem()
// // let tc = {str: }
@@ -204,9 +209,10 @@ class Viewer extends React.Component<IViewerProps> {
// // tcStr += t.str;
// // })
// });
- pageSizes[i] = { width: x.width, height: x.height };
- }));
+ // pageSizes[i] = { width: x.width, height: x.height };
+ })));
}
+ await Promise.all(proms);
runInAction(() =>
Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
@@ -419,20 +425,8 @@ class Viewer extends React.Component<IViewerProps> {
}
}
- renderAnnotation = (anno: Doc, index: number): JSX.Element[] => {
- let annotationDocs = DocListCast(anno.annotations);
- let res = annotationDocs.map(a => {
- let type = NumCast(a.type);
- switch (type) {
- // case AnnotationTypes.Pin:
- // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- case AnnotationTypes.Region:
- return <RegionAnnotation parent={this} document={a} index={index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- default:
- return <div></div>;
- }
- });
- return res;
+ renderAnnotation = (anno: Doc, index: number): JSX.Element => {
+ return <Annotation anno={anno} index={index} parent={this} />
}
@action
@@ -678,116 +672,6 @@ export enum AnnotationTypes {
Region
}
-interface IAnnotationProps {
- x: number;
- y: number;
- width: number;
- height: number;
- index: number;
- parent: Viewer;
- document: Doc;
-}
-
-@observer
-class RegionAnnotation extends React.Component<IAnnotationProps> {
- @observable private _backgroundColor: string = "red";
-
- private _reactionDisposer?: IReactionDisposer;
- private _scrollDisposer?: IReactionDisposer;
- private _mainCont: React.RefObject<HTMLDivElement>;
-
- constructor(props: IAnnotationProps) {
- super(props);
-
- this._mainCont = React.createRef();
- }
-
- componentDidMount() {
- this._reactionDisposer = reaction(
- () => BoolCast(this.props.document.delete),
- () => {
- if (BoolCast(this.props.document.delete)) {
- if (this._mainCont.current) {
- this._mainCont.current.style.display = "none";
- }
- }
- },
- { fireImmediately: true }
- );
-
- this._scrollDisposer = reaction(
- () => this.props.parent.Index,
- () => {
- if (this.props.parent.Index === this.props.index) {
- this.props.parent.scrollTo(this.props.y - 50);
- }
- }
- );
- }
-
- componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- this._scrollDisposer && this._scrollDisposer();
- }
-
- deleteAnnotation = () => {
- let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group && annotation.indexOf(group) !== -1) {
- let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
- this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
- }
-
- if (group) {
- let groupAnnotations = DocListCast(group.annotations);
- groupAnnotations.forEach(anno => anno.delete = true);
- }
-
- PDFMenu.Instance.fadeOut(true);
- }
-
- @action
- onPointerDown = (e: React.PointerEvent) => {
- if (e.button === 0) {
- let targetDoc = Cast(this.props.document.target, Doc, null);
- if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc, true);
- }
- }
- 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.jumpTo(e.clientX, e.clientY, true);
- }
- }
-
- addTag = (key: string, value: string): boolean => {
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group) {
- let valNum = parseInt(value);
- group[key] = isNaN(valNum) ? value : valNum;
- return true;
- }
- return false;
- }
-
- render() {
- return (
- <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
- style={{
- top: this.props.y * scale,
- left: this.props.x * scale,
- width: this.props.width * scale,
- height: this.props.height * scale,
- pointerEvents: "all",
- backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
- }}></div>
- );
- }
-}
-
class SimpleLinkService {
externalLinkTarget: any = null;
externalLinkRel: any = null;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index b0184dd4e..27dcfba08 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -280,6 +280,27 @@ export namespace Doc {
return new Doc;
}
+ export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) {
+ let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined;
+ if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) {
+ return templateLayoutDoc;
+ }
+ // if we have a data doc that doesn't match the layout, then we're rendering a template.
+ // ... which means we change the layout to be an expanded view of the template layout.
+ // This allows the view override the template's properties and be referenceable as its own document.
+
+ let expandedTemplateLayout = templateLayoutDoc["_expanded_" + dataDoc[Id]];
+ if (expandedTemplateLayout instanceof Doc) {
+ return expandedTemplateLayout;
+ }
+ if (expandedTemplateLayout === undefined)
+ setTimeout(() => {
+ templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
+ (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
+ }, 0);
+ return templateLayoutDoc;
+ }
+
export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
const copy = new Doc;
Object.keys(doc).forEach(key => {