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 | 289 | ||||
| -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 | 37 | ||||
| -rw-r--r-- | src/client/views/nodes/Sticky.tsx | 138 | ||||
| -rw-r--r-- | src/client/views/nodes/Timeline.scss | 49 | ||||
| -rw-r--r-- | src/client/views/nodes/Timeline.tsx | 24 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.scss | 2 |
12 files changed, 585 insertions, 359 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..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,145 +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); + 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); } - - 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); } - - 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(); - } + if (this._ref.current) { + this._editorView = new EditorView(this._ref.current, { + state, + dispatchTransaction: this.dispatchTransaction + }); } - componentWillUnmount() { - if (this._editorView) { - this._editorView.destroy(); + 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._reactionDisposer) { - this._reactionDisposer(); - } - } - - 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(); - } - } - - //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(); + } + ); + if (this.props.selectOnLoad) { + this.props.select(); + this._editorView!.focus(); } + } - tooltipMenuPlugin() { - return new Plugin({ - view(_editorView) { - return new TooltipTextMenu(_editorView) - } - }) + componentWillUnmount() { + if (this._editorView) { + this._editorView.destroy(); } - 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; + if (this._reactionDisposer) { + this._reactionDisposer(); } - 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} />) + } + + 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/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 4682753d7..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; } @@ -151,8 +153,7 @@ export class PDFBox extends React.Component<FieldViewProps> { */ makeEditableAndHighlight = (colour: string) => { var range, sel = window.getSelection(); - if (sel) { - if (sel.rangeCount && sel.getRangeAt) { + if (sel && sel.rangeCount && sel.getRangeAt) { range = sel.getRangeAt(0); } document.designMode = "on"; @@ -160,7 +161,7 @@ export class PDFBox extends React.Component<FieldViewProps> { document.execCommand("HiliteColor", false, colour); } - if (range) { + if (range && sel) { sel.removeAllRanges(); sel.addRange(range); @@ -190,7 +191,7 @@ export class PDFBox extends React.Component<FieldViewProps> { } this._pageInfo.divs.push(obj); - }} + } document.designMode = "off"; } @@ -377,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 @@ -428,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 @@ -440,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/Timeline.scss b/src/client/views/nodes/Timeline.scss index 5bf4628c4..d2055c946 100644 --- a/src/client/views/nodes/Timeline.scss +++ b/src/client/views/nodes/Timeline.scss @@ -1,23 +1,40 @@ -.timeline-container{ - top: 10px; - left: 400px; - height: 100px; - width: 500px; - background-color: yellow; +.timeline-container { + height: 100px; + width: 500px; + background-color: rgb(160, 226, 243); position: absolute; + left: 15%; + top: 1%; + font-size: 75%; - .timeline{ - height: 60px; + .timeline { + height: 60px; width: 500px; - bottom: 0px; + bottom: 0px; background-color: grey; - position: absolute; - + position: absolute; } - button{ - height: 40px; - width: 100px; - font-size: 1em; - position:relative; + + .inner { + height: 44px; + width: 484px; + background-color: black; + position: absolute; + margin: 8px; + } + + button { + height: 30px; + width: 100px; + font-size: 1em; + position: relative; + margin: 5px; + } + + input { + height: 30px; + width: 100px; + font-size: 1em; + margin: 5px; } }
\ No newline at end of file diff --git a/src/client/views/nodes/Timeline.tsx b/src/client/views/nodes/Timeline.tsx index 41ac77abc..7872649a0 100644 --- a/src/client/views/nodes/Timeline.tsx +++ b/src/client/views/nodes/Timeline.tsx @@ -5,29 +5,29 @@ import { observable } from "mobx" import { TimelineField } from "../../../fields/TimelineField"; import "./Timeline.scss" - - @observer export class Timeline extends React.Component<TimelineField>{ - private _isRecording = false; - onRecord = (e:React.MouseEvent) => { - this._isRecording = true; + private _isRecording = false; + onRecord = (e: React.MouseEvent) => { + this._isRecording = true; } - onStop = (e:React.MouseEvent) => { - this._isRecording = false; + onStop = (e: React.MouseEvent) => { + this._isRecording = false; } render() { return ( <div> - <div className="timeline-container"> + <div className="timeline-container"> <div className="timeline"> - - </div> - <button onClick = {this.onRecord}>Record</button> - <button onClick = {this.onStop}> Stop </button> + <div className="inner"> + </div> + </div> + <button>Record</button> + <button> Stop </button> + <input placeholder="Time"></input> </div> </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; |
