diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 8 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.scss | 20 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 379 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 18 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 285 | ||||
| -rw-r--r-- | src/client/views/nodes/HistogramBox.scss | 8 | ||||
| -rw-r--r-- | src/client/views/nodes/HistogramBox.tsx | 67 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkBox.tsx | 23 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.scss | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 53 | ||||
| -rw-r--r-- | src/client/views/nodes/Sticky.tsx | 138 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 29 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.scss | 2 |
14 files changed, 617 insertions, 429 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 50dc5a619..1a0f0cbbd 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { computed, trace } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; import { KeyStore } from "../../../fields/KeyStore"; import { NumberField } from "../../../fields/NumberField"; @@ -6,6 +6,7 @@ import { Transform } from "../../util/Transform"; import { DocumentView, DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); +import { thisExpression } from "babel-types"; @observer @@ -65,14 +66,19 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView return <DocumentView {...this.props} ContentScaling={this.contentScaling} ScreenToLocalTransform={this.getTransform} + PanelWidth={this.panelWidth} + PanelHeight={this.panelHeight} /> } + panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth(); + panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight(); render() { return ( <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{ transformOrigin: "left top", transform: this.transform, + pointerEvents: "all", width: this.width, height: this.height, position: "absolute", diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 2f0459f88..77551649c 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -5,7 +5,7 @@ import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; @@ -19,7 +19,7 @@ import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; -import { HistogramBox } from "./HistogramBox"; +import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import React = require("react"); const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -34,6 +34,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); } @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); } + CreateBindings(): JsxBindings { let bindings: JsxBindings = { ...this.props, }; for (const key of this.layoutKeys) { @@ -47,8 +48,13 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { } render() { + let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField); + if (!lkeys || lkeys === FieldWaiting) { + return <p>Error loading layout keys</p>; + } return <JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }} bindings={this.CreateBindings()} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }} + bindings={this.CreateBindings()} jsx={this.layout} showWarnings={true} onError={(test: any) => { console.log(test) }} diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 85a115f1c..5126e69f9 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -1,23 +1,43 @@ @import "../global_variables"; + .documentView-node { position: absolute; + top: 0; + left:0; background: $light-color; //overflow: hidden; + &.minimized { width: 30px; height: 30px; } + .top { background: #232323; height: 20px; cursor: pointer; } + .content { padding: 20px 20px; height: auto; box-sizing: border-box; } + .scroll-box { overflow-y: scroll; height: calc(100% - 20px); } } + +.minimized-box { + height: 10px; + width: 10px; + border-radius: 2px; + background: $dark-color +} + +.minimized-box:hover { + background: $main-accent; + transform: scale(1.15); + cursor: pointer; +}
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fec451b09..b9329f269 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,11 +1,13 @@ import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Field, Opt, FieldWaiting } from "../../../fields/Field"; +import { Field, FieldWaiting, Opt } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; +import { BooleanField } from "../../../fields/BooleanField"; import { TextField } from "../../../fields/TextField"; +import { ServerUtils } from "../../../server/ServerUtil"; import { Utils } from "../../../Utils"; import { Documents } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; @@ -19,11 +21,10 @@ import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); - export interface DocumentViewProps { ContainingCollectionView: Opt<CollectionView>; Document: Document; - AddDocument?: (doc: Document, allowDuplicates: boolean) => void; + AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean; RemoveDocument?: (doc: Document) => boolean; ScreenToLocalTransform: () => Transform; isTopMost: boolean; @@ -34,8 +35,8 @@ export interface DocumentViewProps { SelectOnLoad: boolean; } export interface JsxArgs extends DocumentViewProps { - Keys: { [name: string]: Key } - Fields: { [name: string]: Field } + Keys: { [name: string]: Key }; + Fields: { [name: string]: Field }; } /* @@ -54,16 +55,16 @@ Example usage of this function: } */ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs { - let Keys: { [name: string]: any } = {} - let Fields: { [name: string]: any } = {} + let Keys: { [name: string]: any } = {}; + let Fields: { [name: string]: any } = {}; for (const key of keys) { - let fn = () => { } - Object.defineProperty(fn, "name", { value: key + "Key" }) + let fn = () => { }; + Object.defineProperty(fn, "name", { value: key + "Key" }); Keys[key] = fn; } for (const field of fields) { - let fn = () => { } - Object.defineProperty(fn, "name", { value: field }) + let fn = () => { }; + Object.defineProperty(fn, "name", { value: field }); Fields[field] = fn; } let args: JsxArgs = { @@ -84,58 +85,94 @@ export interface JsxBindings { [prop: string]: any; } - - @observer export class DocumentView extends React.Component<DocumentViewProps> { private _mainCont = React.createRef<HTMLDivElement>(); private _downX: number = 0; private _downY: number = 0; + private _reactionDisposer: Opt<IReactionDisposer>; - @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); } - @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; } - @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); } - @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); } - @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); } - screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect(); + @computed get active(): boolean { + return ( + SelectionManager.IsSelected(this) || + !this.props.ContainingCollectionView || + this.props.ContainingCollectionView.active() + ); + } + @computed get topMost(): boolean { + return ( + !this.props.ContainingCollectionView || + this.props.ContainingCollectionView.collectionViewType == + CollectionViewType.Docking + ); + } + @computed get layout(): string { + return this.props.Document.GetText( + KeyStore.Layout, + "<p>Error loading layout data</p>" + ); + } + @computed get layoutKeys(): Key[] { + return this.props.Document.GetData( + KeyStore.LayoutKeys, + ListField, + new Array<Key>() + ); + } + @computed get layoutFields(): Key[] { + return this.props.Document.GetData( + KeyStore.LayoutFields, + ListField, + new Array<Key>() + ); + } + screenRect = (): ClientRect | DOMRect => + this._mainCont.current + ? this._mainCont.current.getBoundingClientRect() + : new DOMRect(); onPointerDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; if (e.shiftKey && e.buttons === 2) { if (this.props.isTopMost) { this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey); - } - else CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e); + } else + CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e); e.stopPropagation(); } else { if (this.active && !e.isDefaultPrevented()) { e.stopPropagation(); - document.removeEventListener("pointermove", this.onPointerMove) + document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp) + document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); } } - } + }; private dropDisposer?: DragManager.DragDropDisposer; - protected createDropTarget = (ele: HTMLDivElement) => { - - } componentDidMount() { if (this._mainCont.current) { - this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); + this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { + handlers: { drop: this.drop.bind(this) } + }); } - runInAction(() => { - DocumentManager.Instance.DocumentViews.push(this); - }) + runInAction(() => DocumentManager.Instance.DocumentViews.push(this)); this._reactionDisposer = reaction( - () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(), + () => + this.props.ContainingCollectionView && + this.props.ContainingCollectionView.SelectedDocs.slice(), () => { - if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1) + if ( + this.props.ContainingCollectionView && + this.props.ContainingCollectionView.SelectedDocs.indexOf( + this.props.Document.Id + ) != -1 + ) SelectionManager.SelectDoc(this, true); - }); + } + ); } componentDidUpdate() { @@ -143,7 +180,9 @@ export class DocumentView extends React.Component<DocumentViewProps> { this.dropDisposer(); } if (this._mainCont.current) { - this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); + this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { + handlers: { drop: this.drop.bind(this) } + }); } } @@ -151,10 +190,12 @@ export class DocumentView extends React.Component<DocumentViewProps> { if (this.dropDisposer) { this.dropDisposer(); } - runInAction(() => { - DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); - - }) + runInAction(() => + DocumentManager.Instance.DocumentViews.splice( + DocumentManager.Instance.DocumentViews.indexOf(this), + 1 + ) + ); if (this._reactionDisposer) { this._reactionDisposer(); } @@ -162,22 +203,28 @@ export class DocumentView extends React.Component<DocumentViewProps> { startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) { if (this._mainCont.current) { - const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - let dragData = new DragManager.DocumentDragData(this.props.Document); + const [left, top] = this.props + .ScreenToLocalTransform() + .inverse() + .transformPoint(0, 0); + let dragData = new DragManager.DocumentDragData([this.props.Document]); dragData.aliasOnDrop = dropAliasOfDraggedDoc; dragData.xOffset = x - left; dragData.yOffset = y - top; dragData.removeDocument = (dropCollectionView: CollectionView) => { - if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) { + if ( + this.props.RemoveDocument && + this.props.ContainingCollectionView !== dropCollectionView + ) { this.props.RemoveDocument(this.props.Document); } - } - DragManager.StartDocumentDrag(this._mainCont.current, dragData, { + }; + DragManager.StartDocumentDrag([this._mainCont.current], dragData, { handlers: { - dragComplete: action(() => { }), + dragComplete: action(() => { }) }, hideSource: !dropAliasOfDraggedDoc - }) + }); } } @@ -185,8 +232,11 @@ export class DocumentView extends React.Component<DocumentViewProps> { if (e.cancelBubble) { return; } - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - document.removeEventListener("pointermove", this.onPointerMove) + if ( + Math.abs(this._downX - e.clientX) > 3 || + Math.abs(this._downY - e.clientY) > 3 + ) { + document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); if (!this.topMost || e.buttons == 2 || e.altKey) { this.startDragging(e.x, e.y, e.ctrlKey || e.altKey); @@ -194,43 +244,65 @@ export class DocumentView extends React.Component<DocumentViewProps> { } e.stopPropagation(); e.preventDefault(); - } + }; onPointerUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onPointerMove) - document.removeEventListener("pointerup", this.onPointerUp) + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); - if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) { + if (!SelectionManager.IsSelected(this) && + Math.abs(e.clientX - this._downX) < 4 && + Math.abs(e.clientY - this._downY) < 4 + ) { SelectionManager.SelectDoc(this, e.ctrlKey); } - } + }; stopPropogation = (e: React.SyntheticEvent) => { e.stopPropagation(); - } + }; deleteClicked = (): void => { if (this.props.RemoveDocument) { this.props.RemoveDocument(this.props.Document); } - } + }; fieldsClicked = (e: React.MouseEvent): void => { if (this.props.AddDocument) { - this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false); + this.props.AddDocument( + Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), + false + ); } - } + }; fullScreenClicked = (e: React.MouseEvent): void => { CollectionDockingView.Instance.OpenFullScreen(this.props.Document); ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked }); - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } + ContextMenu.Instance.addItem({ + description: "Close Full Screen", + event: this.closeFullScreenClicked + }); + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + }; closeFullScreenClicked = (e: React.MouseEvent): void => { CollectionDockingView.Instance.CloseFullScreen(); ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } + ContextMenu.Instance.addItem({ + description: "Full Screen", + event: this.fullScreenClicked + }); + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + }; + + @action + public minimize = (): void => { + this.props.Document.SetData( + KeyStore.Minimized, + true as boolean, + BooleanField + ); + SelectionManager.DeselectAll(); + }; @action drop = (e: Event, de: DragManager.DropEvent) => { @@ -242,18 +314,37 @@ export class DocumentView extends React.Component<DocumentViewProps> { } let linkDoc: Document = new Document(); - linkDoc.Set(KeyStore.Title, new TextField("New Link")); - linkDoc.Set(KeyStore.LinkDescription, new TextField("")); - linkDoc.Set(KeyStore.LinkTags, new TextField("Default")); - - sourceDoc.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) }); - linkDoc.Set(KeyStore.LinkedToDocs, destDoc); - destDoc.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) }); - linkDoc.Set(KeyStore.LinkedFromDocs, sourceDoc); + destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest => + sourceDoc.GetTAsync(KeyStore.Prototype, Document).then(protoSrc => + runInAction(() => { + linkDoc.Set(KeyStore.Title, new TextField("New Link")); + linkDoc.Set(KeyStore.LinkDescription, new TextField("")); + linkDoc.Set(KeyStore.LinkTags, new TextField("Default")); + let dstTarg = protoDest ? protoDest : destDoc; + let srcTarg = protoSrc ? protoSrc : sourceDoc; + linkDoc.Set(KeyStore.LinkedToDocs, dstTarg); + linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg); + dstTarg.GetOrCreateAsync( + KeyStore.LinkedFromDocs, + ListField, + field => { + (field as ListField<Document>).Data.push(linkDoc); + } + ); + srcTarg.GetOrCreateAsync( + KeyStore.LinkedToDocs, + ListField, + field => { + (field as ListField<Document>).Data.push(linkDoc); + } + ); + }) + ) + ); e.stopPropagation(); } - } + }; onDrop = (e: React.DragEvent) => { if (e.isDefaultPrevented()) { @@ -267,22 +358,49 @@ export class DocumentView extends React.Component<DocumentViewProps> { e.stopPropagation(); e.preventDefault(); } - } + }; @action onContextMenu = (e: React.MouseEvent): void => { e.stopPropagation(); - let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3; + let moved = + Math.abs(this._downX - e.clientX) > 3 || + Math.abs(this._downY - e.clientY) > 3; if (moved || e.isDefaultPrevented()) { - e.preventDefault() + e.preventDefault(); return; } - e.preventDefault() + e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked }) - ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) }) - ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }) + if (!this.isMinimized()) { + ContextMenu.Instance.addItem({ + description: "Minimize", + event: this.minimize + }); + } + ContextMenu.Instance.addItem({ + description: "Full Screen", + event: this.fullScreenClicked + }); + ContextMenu.Instance.addItem({ + description: "Fields", + event: this.fieldsClicked + }); + ContextMenu.Instance.addItem({ + description: "Center", + event: () => this.props.focus(this.props.Document) + }); + ContextMenu.Instance.addItem({ + description: "Open Right", + event: () => + CollectionDockingView.Instance.AddRightSplit(this.props.Document) + }); + ContextMenu.Instance.addItem({ + description: "Copy URL", + event: () => { + Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id)); + } + }); ContextMenu.Instance.addItem({ description: "Copy ID", event: () => { @@ -290,52 +408,95 @@ export class DocumentView extends React.Component<DocumentViewProps> { } }); //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); if (!this.topMost) { // DocumentViews should stop propagation of this event e.stopPropagation(); } - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + ContextMenu.Instance.addItem({ + description: "Delete", + event: this.deleteClicked + }); + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); SelectionManager.SelectDoc(this, e.ctrlKey); - } + }; + isMinimized = () => { + let field = this.props.Document.GetT(KeyStore.Minimized, BooleanField); + if (field && field !== FieldWaiting) { + return field.Data; + } + }; + + @action + expand = () => { + this.props.Document.SetData( + KeyStore.Minimized, + false as boolean, + BooleanField + ); + }; isSelected = () => { return SelectionManager.IsSelected(this); - } + }; select = (ctrlPressed: boolean) => { - SelectionManager.SelectDoc(this, ctrlPressed) - } + SelectionManager.SelectDoc(this, ctrlPressed); + }; render() { if (!this.props.Document) { - return (null); - } - let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField); - if (!lkeys || lkeys === FieldWaiting) { - return <p>Error loading layout keys</p>; + return null; } + var scaling = this.props.ContentScaling(); var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0); - var backgroundcolor = this.props.Document.GetText(KeyStore.BackgroundColor, ""); - return ( - <div className="documentView-node" ref={this._mainCont} - style={{ - background: backgroundcolor, - width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%", - height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%", - transformOrigin: "left top", - transform: `scale(${scaling} , ${scaling})` - }} - onDrop={this.onDrop} - onContextMenu={this.onContextMenu} - onPointerDown={this.onPointerDown} > - <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} /> - </div > - ) + + if (this.isMinimized()) { + return ( + <div + className="minimized-box" + ref={this._mainCont} + style={{ + transformOrigin: "left top", + transform: `scale(${scaling} , ${scaling})` + }} + onClick={this.expand} + onDrop={this.onDrop} + onPointerDown={this.onPointerDown} + /> + ); + } else { + var backgroundcolor = this.props.Document.GetText( + KeyStore.BackgroundColor, + "" + ); + return ( + <div + className="documentView-node" + ref={this._mainCont} + style={{ + background: backgroundcolor, + width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%", + height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%", + transformOrigin: "left top", + transform: `scale(${scaling} , ${scaling})` + }} + onDrop={this.onDrop} + onContextMenu={this.onContextMenu} + onPointerDown={this.onPointerDown} + > + <DocumentContentsView + {...this.props} + isSelected={this.isSelected} + select={this.select} + layoutKey={KeyStore.Layout} + /> + </div> + ); + } } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index f6343c631..4e83ec7b9 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -16,6 +16,9 @@ import { VideoBox } from "./VideoBox"; import { AudioBox } from "./AudioBox"; import { AudioField } from "../../../fields/AudioField"; import { ListField } from "../../../fields/ListField"; +import { DocumentContentsView } from "./DocumentContentsView"; +import { Transform } from "../../util/Transform"; +import { KeyStore } from "../../../fields/KeyStore"; // @@ -65,7 +68,20 @@ export class FieldView extends React.Component<FieldViewProps> { return <AudioBox {...this.props} /> } else if (field instanceof Document) { - return <div>{field.Title}</div> + return (<DocumentContentsView Document={field} + AddDocument={undefined} + RemoveDocument={undefined} + ScreenToLocalTransform={() => Transform.Identity} + ContentScaling={() => 1} + PanelWidth={() => 100} + PanelHeight={() => 100} + isTopMost={true} + SelectOnLoad={false} + focus={() => { }} + isSelected={() => false} + select={() => false} + layoutKey={KeyStore.Layout} + ContainingCollectionView={undefined} />) } else if (field instanceof ListField) { return (<div> diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ba9bd9566..0c0efc437 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -3,28 +3,25 @@ import { baseKeymap } from "prosemirror-commands"; import { history, redo, undo } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { schema } from "../../util/RichTextSchema"; -import { EditorState, Transaction, } from "prosemirror-state"; +import { EditorState, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Opt, FieldWaiting } from "../../../fields/Field"; import "./FormattedTextBox.scss"; -import React = require("react") +import React = require("react"); import { RichTextField } from "../../../fields/RichTextField"; import { FieldViewProps, FieldView } from "./FieldView"; -import { Plugin } from 'prosemirror-state' -import { Decoration, DecorationSet } from 'prosemirror-view' -import { TooltipTextMenu } from "../../util/TooltipTextMenu" +import { Plugin } from "prosemirror-state"; +import { Decoration, DecorationSet } from "prosemirror-view"; +import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { ContextMenu } from "../../views/ContextMenu"; import { inpRules } from "../../util/RichTextRules"; const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); - - - // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document // // HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"} -// +// // In Code, the node's HTML is specified in the document's parameterized structure as: // document.SetField(KeyStore.Layout, "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />"); // and the node's binding to the specified document KEYNAME as: @@ -33,141 +30,161 @@ const { menuBar } = require("prosemirror-menu"); // 'fieldKey' property to the Key stored in LayoutKeys // and 'doc' property to the document that is being rendered // -// When rendered() by React, this extracts the TextController from the Document stored at the -// specified Key and assigns it to an HTML input node. When changes are made to this node, +// When rendered() by React, this extracts the TextController from the Document stored at the +// specified Key and assigns it to an HTML input node. When changes are made to this node, // this will edit the document and assign the new value to that field. //] export class FormattedTextBox extends React.Component<FieldViewProps> { - - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) } - private _ref: React.RefObject<HTMLDivElement>; - private _editorView: Opt<EditorView>; - private _reactionDisposer: Opt<IReactionDisposer>; - - constructor(props: FieldViewProps) { - super(props); - - this._ref = React.createRef(); - this.onChange = this.onChange.bind(this); - } - - dispatchTransaction = (tx: Transaction) => { - if (this._editorView) { - const state = this._editorView.state.apply(tx); - this._editorView.updateState(state); - const { doc, fieldKey } = this.props; - doc.SetDataOnPrototype(fieldKey, JSON.stringify(state.toJSON()), RichTextField); - // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); - } - } - - componentDidMount() { - let state: EditorState; - const config = { - schema, - inpRules, //these currently don't do anything, but could eventually be helpful - plugins: [ - history(), - keymap({ "Mod-z": undo, "Mod-y": redo }), - keymap(baseKeymap), - this.tooltipMenuPlugin() - ] - }; - - let field = this.props.doc.GetT(this.props.fieldKey, RichTextField); - if (field && field != FieldWaiting && field.Data) { - state = EditorState.fromJSON(config, JSON.parse(field.Data)); - } else { - state = EditorState.create(config); - } - if (this._ref.current) { - this._editorView = new EditorView(this._ref.current, { - state, - dispatchTransaction: this.dispatchTransaction - }); - } - - this._reactionDisposer = reaction(() => { - const field = this.props.doc.GetT(this.props.fieldKey, RichTextField); - return field && field != FieldWaiting ? field.Data : undefined; - }, (field) => { - if (field && this._editorView) { - this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field))); - } - }) - if (this.props.selectOnLoad) { - this.props.select(); - this._editorView!.focus(); - } + public static LayoutString(fieldStr: string = "DataKey") { + return FieldView.LayoutString(FormattedTextBox, fieldStr); + } + private _ref: React.RefObject<HTMLDivElement>; + private _editorView: Opt<EditorView>; + private _reactionDisposer: Opt<IReactionDisposer>; + + constructor(props: FieldViewProps) { + super(props); + + this._ref = React.createRef(); + this.onChange = this.onChange.bind(this); + } + + dispatchTransaction = (tx: Transaction) => { + if (this._editorView) { + const state = this._editorView.state.apply(tx); + this._editorView.updateState(state); + const { doc, fieldKey } = this.props; + doc.SetDataOnPrototype( + fieldKey, + JSON.stringify(state.toJSON()), + RichTextField + ); + // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); } - - componentWillUnmount() { - if (this._editorView) { - this._editorView.destroy(); - } - if (this._reactionDisposer) { - this._reactionDisposer(); - } + }; + + componentDidMount() { + let state: EditorState; + const config = { + schema, + inpRules, //these currently don't do anything, but could eventually be helpful + plugins: [ + history(), + keymap({ "Mod-z": undo, "Mod-y": redo }), + keymap(baseKeymap), + this.tooltipMenuPlugin() + ] + }; + + let field = this.props.doc.GetT(this.props.fieldKey, RichTextField); + if (field && field != FieldWaiting && field.Data) { + state = EditorState.fromJSON(config, JSON.parse(field.Data)); + } else { + state = EditorState.create(config); } - - shouldComponentUpdate() { - return false; + if (this._ref.current) { + this._editorView = new EditorView(this._ref.current, { + state, + dispatchTransaction: this.dispatchTransaction + }); } - @action - onChange(e: React.ChangeEvent<HTMLInputElement>) { - const { fieldKey, doc } = this.props; - doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value)) - // doc.SetData(fieldKey, e.target.value, RichTextField); - } - onPointerDown = (e: React.PointerEvent): void => { - if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { - e.stopPropagation(); + this._reactionDisposer = reaction( + () => { + const field = this.props.doc.GetT(this.props.fieldKey, RichTextField); + return field && field != FieldWaiting ? field.Data : undefined; + }, + field => { + if (field && this._editorView) { + this._editorView.updateState( + EditorState.fromJSON(config, JSON.parse(field)) + ); } + } + ); + if (this.props.selectOnLoad) { + this.props.select(); + this._editorView!.focus(); } + } - //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE - textCapability = (e: React.MouseEvent): void => { + componentWillUnmount() { + if (this._editorView) { + this._editorView.destroy(); } - - specificContextMenu = (e: React.MouseEvent): void => { - ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability }); - // ContextMenu.Instance.addItem({ - // description: "Submenu", - // items: [ - // { - // description: "item 1", event: - // }, - // { - // description: "item 2", event: - // } - // ] - // }) - // e.stopPropagation() - - } - - onPointerWheel = (e: React.WheelEvent): void => { - e.stopPropagation(); - } - - tooltipMenuPlugin() { - return new Plugin({ - view(_editorView) { - return new TooltipTextMenu(_editorView) - } - }) - } - - onKeyPress(e: React.KeyboardEvent) { - e.stopPropagation(); + if (this._reactionDisposer) { + this._reactionDisposer(); } - render() { - return (<div className="formattedTextBox-cont" - onKeyPress={this.onKeyPress} - onPointerDown={this.onPointerDown} - onContextMenu={this.specificContextMenu} - onWheel={this.onPointerWheel} - ref={this._ref} />) + } + + shouldComponentUpdate() { + return false; + } + + @action + onChange(e: React.ChangeEvent<HTMLInputElement>) { + const { fieldKey, doc } = this.props; + doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value)); + // doc.SetData(fieldKey, e.target.value, RichTextField); + } + onPointerDown = (e: React.PointerEvent): void => { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + e.stopPropagation(); } -}
\ No newline at end of file + }; + + //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE + textCapability = (e: React.MouseEvent): void => {}; + + specificContextMenu = (e: React.MouseEvent): void => { + ContextMenu.Instance.addItem({ + description: "Text Capability", + event: this.textCapability + }); + + // ContextMenu.Instance.addItem({ + // description: "Submenu", + // items: [ + // { + // description: "item 1", event: + // }, + // { + // description: "item 2", event: + // } + // ] + // }) + // e.stopPropagation() + }; + + onPointerWheel = (e: React.WheelEvent): void => { + e.stopPropagation(); + }; + + tooltipMenuPlugin() { + return new Plugin({ + view(_editorView) { + return new TooltipTextMenu(_editorView); + } + }); + } + onKeyPress(e: React.KeyboardEvent) { + e.stopPropagation(); + // stop propagation doesn't seem to stop propagation of native keyboard events. + // so we set a flag on the native event that marks that the event's been handled. + // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true; + } + render() { + return ( + <div + className="formattedTextBox-cont" + onKeyDown={this.onKeyPress} + onKeyPress={this.onKeyPress} + onPointerDown={this.onPointerDown} + onContextMenu={this.specificContextMenu} + // tfs: do we need this event handler + onWheel={this.onPointerWheel} + ref={this._ref} + /> + ); + } +} diff --git a/src/client/views/nodes/HistogramBox.scss b/src/client/views/nodes/HistogramBox.scss deleted file mode 100644 index 04bf1d732..000000000 --- a/src/client/views/nodes/HistogramBox.scss +++ /dev/null @@ -1,8 +0,0 @@ -.histogrambox-container { - padding: 0vw; - position: relative; - text-align: center; - width: 100%; - height: 100%; - } -
\ No newline at end of file diff --git a/src/client/views/nodes/HistogramBox.tsx b/src/client/views/nodes/HistogramBox.tsx deleted file mode 100644 index 223fdf0d8..000000000 --- a/src/client/views/nodes/HistogramBox.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React = require("react") -import { observer } from "mobx-react"; -import { FieldView, FieldViewProps } from './FieldView'; -import "./VideoBox.scss"; -import { observable, reaction } from "mobx"; -import { HistogramOperation } from "../../northstar/operations/HistogramOperation"; -import { Main } from "../Main"; -import { ColumnAttributeModel } from "../../northstar/core/attribute/AttributeModel"; -import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel"; -import { AggregateFunction, HistogramResult, DoubleValueAggregateResult } from "../../northstar/model/idea/idea"; -import { ModelHelpers } from "../../northstar/model/ModelHelpers"; - -@observer -export class HistogramBox extends React.Component<FieldViewProps> { - - public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr) } - - constructor(props: FieldViewProps) { - super(props); - } - - @observable _histoResult?: HistogramResult; - _histoOp?: HistogramOperation; - - componentDidMount() { - Main.Instance.GetAllNorthstarColumnAttributes().map(a => { - if (a.displayName == this.props.doc.Title) { - var atmod = new ColumnAttributeModel(a); - this._histoOp = new HistogramOperation(new AttributeTransformationModel(atmod, AggregateFunction.None), - new AttributeTransformationModel(atmod, AggregateFunction.Count), - new AttributeTransformationModel(atmod, AggregateFunction.Count)); - reaction(() => [this._histoOp && this._histoOp.Result], - () => this._histoResult = this._histoOp ? this._histoOp.Result as HistogramResult : undefined - ); - this._histoOp.Update(); - } - }) - } - - twoString() { - let str = ""; - if (this._histoResult && !this._histoResult.isEmpty) { - for (let key in this._histoResult.bins) { - if (this._histoResult.bins.hasOwnProperty(key)) { - let bin = this._histoResult.bins[key]; - str += JSON.stringify(bin.binIndex!.toJSON()) + " = "; - let valueAggregateKey = ModelHelpers.CreateAggregateKey(this._histoOp!.V, this._histoResult, ModelHelpers.AllBrushIndex(this._histoResult)); - let value = ModelHelpers.GetAggregateResult(bin, valueAggregateKey) as DoubleValueAggregateResult; - if (value && value.hasResult && value.result) { - str += value.result; - } - } - } - } - return str; - } - - render() { - if (!this._histoResult) - return (null); - return ( - <div className="histogrambox-container"> - `HISTOGRAM RESULT : ${this.twoString()}` - </div> - ) - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index dd2f71b59..e81f8fec7 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -17,6 +17,8 @@ import { faEye } from '@fortawesome/free-solid-svg-icons'; import { faEdit } from '@fortawesome/free-solid-svg-icons'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { undoBatch } from "../../util/UndoManager"; +import { FieldWaiting } from "../../../fields/Field"; +import { NumberField } from "../../../fields/NumberField"; library.add(faEye); @@ -39,9 +41,26 @@ export class LinkBox extends React.Component<Props> { e.stopPropagation(); let docView = DocumentManager.Instance.getDocumentView(this.props.pairedDoc); if (docView) { - docView.props.focus(this.props.pairedDoc); + docView.props.focus(docView.props.Document); } else { - CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc) + this.props.pairedDoc.GetAsync(KeyStore.AnnotationOn, (contextDoc: any) => { + if (!contextDoc) { + CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc.MakeDelegate()); + } else if (contextDoc instanceof Document) { + this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => { + contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => { + if (pfield != cfield) + contextDoc.SetNumber(KeyStore.CurPage, pfield.Data); + let contextView = DocumentManager.Instance.getDocumentView(contextDoc); + if (contextView) { + contextView.props.focus(contextDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(contextDoc); + } + }) + }); + } + }); } } diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index ad947afd5..830dfe6c6 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -1,12 +1,16 @@ .react-pdf__Page { transform-origin: left top; position: absolute; + top: 0; + left:0; } .react-pdf__Document { position: absolute; } .pdfBox-buttonTray { position:absolute; + top: 0; + left:0; z-index: 25; } .pdfBox-contentContainer { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3a0ef2d32..f9f5bc8f8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, observable, reaction, IReactionDisposer, trace } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; @@ -10,6 +10,7 @@ import { FieldWaiting, Opt } from '../../../fields/Field'; import { ImageField } from '../../../fields/ImageField'; import { KeyStore } from '../../../fields/KeyStore'; import { PDFField } from '../../../fields/PDFField'; +import { RouteStore } from "../../../server/RouteStore"; import { Utils } from '../../../Utils'; import { Annotation } from './Annotation'; import { FieldView, FieldViewProps } from './FieldView'; @@ -17,7 +18,7 @@ import "./ImageBox.scss"; import "./PDFBox.scss"; import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here import React = require("react") -import { RouteStore } from "../../../server/RouteStore"; +import { SelectionManager } from "../../util/SelectionManager"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -57,10 +58,11 @@ export class PDFBox extends React.Component<FieldViewProps> { private _mainDiv = React.createRef<HTMLDivElement>() private _pdf = React.createRef<HTMLCanvasElement>(); + @observable private _renderAsSvg = true; + //very useful for keeping track of X and y position throughout the PDF Canvas private initX: number = 0; private initY: number = 0; - private initPage: boolean = false; //checks if tool is on private _toolOn: boolean = false; //checks if tool is on @@ -87,18 +89,16 @@ export class PDFBox extends React.Component<FieldViewProps> { @observable private _interactive: boolean = false; @observable private _loaded: boolean = false; - @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 1); } + @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); } componentDidMount() { this._reactionDisposer = reaction( - () => this.curPage, + () => [SelectionManager.SelectedDocuments().slice()], () => { - if (this.curPage && this.initPage) { + if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage && !this.props.isSelected()) { this.saveThumbnail(); this._interactive = true; - } else { - if (this.curPage > 0) - this.initPage = true; } }, { fireImmediately: true }); @@ -378,17 +378,21 @@ export class PDFBox extends React.Component<FieldViewProps> { @action saveThumbnail = () => { + this._renderAsSvg = false; setTimeout(() => { var me = this; - htmlToImage.toPng(this._mainDiv.current!, - { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 }) - .then(function (dataUrl: string) { + let nwidth = me.props.doc.GetNumber(KeyStore.NativeWidth, 0); + let nheight = me.props.doc.GetNumber(KeyStore.NativeHeight, 0); + htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) + .then(action((dataUrl: string) => { me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField); - }) + me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1)); + me._renderAsSvg = true; + })) .catch(function (error: any) { console.error('oops, something went wrong!', error); }); - }, 1000); + }, 250); } @action @@ -428,21 +432,16 @@ export class PDFBox extends React.Component<FieldViewProps> { this.props.doc.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0)); this.props.doc.SetNumber(KeyStore.NativeHeight, nativeHeight); } - if (!this.props.doc.GetT(KeyStore.Thumbnail, ImageField)) { - this.saveThumbnail(); - } } @computed get pdfContent() { let page = this.curPage; - if (page == 0) - page = 1; const renderHeight = 2400; let pdfUrl = this.props.doc.GetT(this.props.fieldKey, PDFField); let xf = this.props.doc.GetNumber(KeyStore.NativeHeight, 0) / renderHeight; return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}> - <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`}> + <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : ""}> <Measure onResize={this.setScaling}> {({ measureRef }) => <div className="pdfBox-page" ref={measureRef}> @@ -464,19 +463,17 @@ export class PDFBox extends React.Component<FieldViewProps> { return [ this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element), this._currAnno.map((element: any) => element), - <div key="pdfBox-contentShell"> - {this.pdfContent} - {proxy} - </div> + this.pdfContent, + proxy ]; } @computed get imageProxyRenderer() { - let field = this.props.doc.Get(KeyStore.Thumbnail); - if (field) { - let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - field instanceof ImageField ? field.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + let thumbField = this.props.doc.Get(KeyStore.Thumbnail); + if (thumbField) { + let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : + thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; return <img src={path} width="100%" />; } return (null); diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx index d57dd5c0b..4a4d69e90 100644 --- a/src/client/views/nodes/Sticky.tsx +++ b/src/client/views/nodes/Sticky.tsx @@ -1,83 +1,83 @@ -import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import React = require("react") -import { observer } from "mobx-react" -import 'react-pdf/dist/Page/AnnotationLayer.css' +import "react-image-lightbox/style.css"; // This only needs to be imported once in your app +import React = require("react"); +import { observer } from "mobx-react"; +import "react-pdf/dist/Page/AnnotationLayer.css"; interface IProps { - Height: number; - Width: number; - X: number; - Y: number; + Height: number; + Width: number; + X: number; + Y: number; } /** - * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file. - * Improvements that could be made: maybe store line array and store that somewhere for future rerendering. - * - * Written By: Andrew Kim + * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file. + * Improvements that could be made: maybe store line array and store that somewhere for future rerendering. + * + * Written By: Andrew Kim */ @observer export class Sticky extends React.Component<IProps> { + private initX: number = 0; + private initY: number = 0; - private initX: number = 0; - private initY: number = 0; + private _ref = React.createRef<HTMLCanvasElement>(); + private ctx: any; //context that keeps track of sticky canvas - private _ref = React.createRef<HTMLCanvasElement>(); - private ctx: any; //context that keeps track of sticky canvas - - /** - * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas - */ - drawDown = (e: React.PointerEvent) => { - if (this._ref.current) { - this.ctx = this._ref.current.getContext("2d"); - let mouse = e.nativeEvent; - this.initX = mouse.offsetX; - this.initY = mouse.offsetY; - this.ctx.beginPath(); - this.ctx.lineTo(this.initX, this.initY); - this.ctx.strokeStyle = "black"; - document.addEventListener("pointermove", this.drawMove); - document.addEventListener("pointerup", this.drawUp); - } + /** + * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas + */ + drawDown = (e: React.PointerEvent) => { + if (this._ref.current) { + this.ctx = this._ref.current.getContext("2d"); + let mouse = e.nativeEvent; + this.initX = mouse.offsetX; + this.initY = mouse.offsetY; + this.ctx.beginPath(); + this.ctx.lineTo(this.initX, this.initY); + this.ctx.strokeStyle = "black"; + document.addEventListener("pointermove", this.drawMove); + document.addEventListener("pointerup", this.drawUp); } + }; - //when user drags - drawMove = (e: PointerEvent): void => { - //x and y mouse movement - let x = this.initX += e.movementX, - y = this.initY += e.movementY; - //connects the point - this.ctx.lineTo(x, y); - this.ctx.stroke(); - - } + //when user drags + drawMove = (e: PointerEvent): void => { + //x and y mouse movement + let x = (this.initX += e.movementX), + y = (this.initY += e.movementY); + //connects the point + this.ctx.lineTo(x, y); + this.ctx.stroke(); + }; - /** - * when user lifts the mouse, the drawing ends - */ - drawUp = (e: PointerEvent) => { - this.ctx.closePath(); - console.log(this.ctx); - document.removeEventListener("pointermove", this.drawMove); - } + /** + * when user lifts the mouse, the drawing ends + */ + drawUp = (e: PointerEvent) => { + this.ctx.closePath(); + console.log(this.ctx); + document.removeEventListener("pointermove", this.drawMove); + }; - render() { - return ( - <div onPointerDown={this.drawDown}> - <canvas ref={this._ref} height={this.props.Height} width={this.props.Width} - style={{ - position: "absolute", - top: "20px", - left: "0px", - zIndex: 1, - background: "yellow", - transform: `translate(${this.props.X}px, ${this.props.Y}px)`, - opacity: 0.4 - }} - /> - - </div> - ); - } -}
\ No newline at end of file + render() { + return ( + <div onPointerDown={this.drawDown}> + <canvas + ref={this._ref} + height={this.props.Height} + width={this.props.Width} + style={{ + position: "absolute", + top: "20px", + left: "0px", + zIndex: 1, + background: "yellow", + transform: `translate(${this.props.X}px, ${this.props.Y}px)`, + opacity: 0.4 + }} + /> + </div> + ); + } +} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 09ae95183..7c0db83a8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,23 +1,27 @@ import React = require("react") import { observer } from "mobx-react"; -import { FieldWaiting } from '../../../fields/Field'; +import { FieldWaiting, Opt } from '../../../fields/Field'; import { VideoField } from '../../../fields/VideoField'; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; import Measure from "react-measure"; -import { action, trace, observable } from "mobx"; +import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx"; import { KeyStore } from "../../../fields/KeyStore"; import { number } from "prop-types"; @observer export class VideoBox extends React.Component<FieldViewProps> { + private _reactionDisposer: Opt<IReactionDisposer>; + private _videoRef = React.createRef<HTMLVideoElement>() public static LayoutString() { return FieldView.LayoutString(VideoBox) } constructor(props: FieldViewProps) { super(props); } + @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + _loaded: boolean = false; @@ -39,7 +43,17 @@ export class VideoBox extends React.Component<FieldViewProps> { } } + get player(): HTMLVideoElement | undefined { + return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined; + } + @action + setVideoRef = (vref: HTMLVideoElement | null) => { + if (this.curPage >= 0 && vref) { + vref!.currentTime = this.curPage; + (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage; + } + } render() { let field = this.props.doc.GetT(this.props.fieldKey, VideoField); @@ -47,15 +61,16 @@ export class VideoBox extends React.Component<FieldViewProps> { return <div>Loading</div> } let path = field.Data.href; - - //setTimeout(action(() => this._loaded = true), 500); + trace(); return ( <Measure onResize={this.setScaling}> {({ measureRef }) => - <video className="videobox-cont" onClick={() => { }} ref={measureRef}> - <source src={path} type="video/mp4" /> - Not supported. + <div style={{ width: "100%", height: "auto" }} ref={measureRef}> + <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}> + <source src={path} type="video/mp4" /> + Not supported. </video> + </div> } </Measure> ) diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index a535b2638..c73bc0c47 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -2,6 +2,8 @@ .webBox-cont { padding: 0vw; position: absolute; + top: 0; + left:0; width: 100%; height: 100%; overflow: scroll; |
