diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.scss | 20 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 365 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 151 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.scss | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 30 | ||||
| -rw-r--r-- | src/client/views/nodes/Sticky.tsx | 138 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.scss | 2 |
10 files changed, 488 insertions, 238 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index d52b662bd..1a0f0cbbd 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -66,8 +66,12 @@ 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 ( 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 1195128dc..7514e782d 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"; @@ -18,8 +20,6 @@ import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); -import { ServerUtils } from "../../../server/ServerUtil"; - export interface DocumentViewProps { ContainingCollectionView: Opt<CollectionView>; @@ -35,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 }; } /* @@ -55,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 = { @@ -85,53 +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; 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() { @@ -139,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) } + }); } } @@ -147,7 +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(); } @@ -155,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); + 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, x, y, { handlers: { - dragComplete: action(() => { }), + dragComplete: action(() => { }) }, hideSource: !dropAliasOfDraggedDoc - }) + }); } } @@ -178,52 +232,77 @@ 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); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey); } } 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) => { @@ -235,23 +314,37 @@ export class DocumentView extends React.Component<DocumentViewProps> { } let linkDoc: Document = new Document(); - 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")); + 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) }) - })) - ) + 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()) { @@ -265,22 +358,43 @@ 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: () => { @@ -294,48 +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); + 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/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 512ad7d70..e59179874 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -2,29 +2,26 @@ import { action, IReactionDisposer, reaction } from "mobx"; 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, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { Opt, FieldWaiting } from "../../../fields/Field"; -import "./FormattedTextBox.scss"; -import React = require("react") +import { FieldWaiting, Opt } from "../../../fields/Field"; +import { KeyStore } from "../../../fields/KeyStore"; 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 { ContextMenu } from "../../views/ContextMenu"; import { inpRules } from "../../util/RichTextRules"; +import { schema } from "../../util/RichTextSchema"; +import { TooltipTextMenu } from "../../util/TooltipTextMenu"; +import { ContextMenu } from "../../views/ContextMenu"; +import { Main } from "../Main"; +import { FieldView, FieldViewProps } from "./FieldView"; +import "./FormattedTextBox.scss"; +import React = require("react"); 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,16 +30,18 @@ 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) } + public static LayoutString(fieldStr: string = "DataKey") { + return FieldView.LayoutString(FormattedTextBox, fieldStr); + } private _ref: React.RefObject<HTMLDivElement>; private _editorView: Opt<EditorView>; private _reactionDisposer: Opt<IReactionDisposer>; + private _inputReactionDisposer: Opt<IReactionDisposer>; constructor(props: FieldViewProps) { super(props); @@ -55,14 +54,19 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { 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); + this.FieldDoc.SetDataOnPrototype( + this.FieldKey, + JSON.stringify(state.toJSON()), + RichTextField + ); // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); } - } + }; + + get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.doc; } + get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; } componentDidMount() { - let state: EditorState; const config = { schema, inpRules, //these currently don't do anything, but could eventually be helpful @@ -74,7 +78,37 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { ] }; - let field = this.props.doc.GetT(this.props.fieldKey, RichTextField); + if (this.props.fieldKey === KeyStore.Archives) { + this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id, + () => { + if (this._editorView) + this._editorView!.destroy(); + + this.setupEditor(config); + } + ) + } + + this._reactionDisposer = reaction( + () => { + const field = this.FieldDoc.GetT(this.FieldKey, RichTextField); + return field && field != FieldWaiting ? field.Data : undefined; + }, + field => { + if (field && this._editorView) { + this._editorView.updateState( + EditorState.fromJSON(config, JSON.parse(field)) + ); + } + } + ); + this.setupEditor(config); + } + + private setupEditor(config: any) { + + let state: EditorState; + let field = this.FieldDoc.GetT(this.FieldKey, RichTextField); if (field && field != FieldWaiting && field.Data) { state = EditorState.fromJSON(config, JSON.parse(field.Data)); } else { @@ -87,14 +121,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { }); } - 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(); @@ -108,6 +134,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { if (this._reactionDisposer) { this._reactionDisposer(); } + if (this._inputReactionDisposer) { + this._inputReactionDisposer(); + } } shouldComponentUpdate() { @@ -117,21 +146,41 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { @action onChange(e: React.ChangeEvent<HTMLInputElement>) { const { fieldKey, doc } = this.props; - doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value)) + 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(); } + if (e.buttons === 1 && this.props.fieldKey !== KeyStore.Archives) + e.preventDefault(); + }; + onPointerUp = (e: React.PointerEvent): void => { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + e.stopPropagation(); + } + if (this.props.fieldKey !== KeyStore.Archives) { + e.preventDefault(); + Main.Instance.SetTextDoc(this.props.doc, this._ref.current!); + } + }; + + onFocused = (e: React.FocusEvent): void => { + if (this.props.fieldKey !== KeyStore.Archives) { + Main.Instance.SetTextDoc(this.props.doc, this._ref.current!); + } } //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE - textCapability = (e: React.MouseEvent): void => { - } + textCapability = (e: React.MouseEvent): void => { }; specificContextMenu = (e: React.MouseEvent): void => { - ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability }); + ContextMenu.Instance.addItem({ + description: "Text Capability", + event: this.textCapability + }); + // ContextMenu.Instance.addItem({ // description: "Submenu", // items: [ @@ -144,19 +193,18 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { // ] // }) // e.stopPropagation() - - } + }; onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); - } + }; tooltipMenuPlugin() { return new Plugin({ view(_editorView) { - return new TooltipTextMenu(_editorView) + return new TooltipTextMenu(_editorView); } - }) + }); } onKeyPress(e: React.KeyboardEvent) { e.stopPropagation(); @@ -165,13 +213,18 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { // (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} />) + return ( + <div + className="formattedTextBox-cont" + onKeyDown={this.onKeyPress} + onKeyPress={this.onKeyPress} + onPointerUp={this.onPointerUp} + onPointerDown={this.onPointerDown} + onContextMenu={this.specificContextMenu} + // tfs: do we need this event handler + onWheel={this.onPointerWheel} + ref={this._ref} + /> + ); } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 283c1f732..9bd6c1052 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -40,11 +40,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> { } let realDoc = doc; - let script = CompileScript(this._valueInput, undefined, true); + let script = CompileScript(this._valueInput, { addReturn: true }); if (!script.compiled) { return; } - let field = script(); + let res = script.run(); + if (!res.success) return; + const field = res.result; if (field instanceof Field) { realDoc.Set(new Key(this._keyInput), field); } else { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 7ed5ee272..5647f45bf 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -76,11 +76,13 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { return field || ""; }} SetValue={(value: string) => { - let script = CompileScript(value, undefined, true); + let script = CompileScript(value, { addReturn: true }); if (!script.compiled) { return false; } - let field = script(); + let res = script.run(); + if (!res.success) return false; + const field = res.result; if (field instanceof Field) { props.doc.Set(props.fieldKey, field); return true; 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 12208714d..7039b0c41 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, keys } 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,8 +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 { NumberField } from "../../../fields/NumberField"; +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, @@ -58,6 +58,8 @@ 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; @@ -92,9 +94,9 @@ export class PDFBox extends React.Component<FieldViewProps> { componentDidMount() { this._reactionDisposer = reaction( - () => [this.curPage, this.thumbnailPage], + () => [SelectionManager.SelectedDocuments().slice()], () => { - if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) { + if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage && !this.props.isSelected()) { this.saveThumbnail(); this._interactive = true; } @@ -376,18 +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 @@ -427,9 +432,6 @@ 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 @@ -439,7 +441,7 @@ export class PDFBox extends React.Component<FieldViewProps> { 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}> 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/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; |
