diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/Main.tsx | 60 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionFreeFormView.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionViewBase.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 47 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.scss | 70 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 69 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 31 |
9 files changed, 246 insertions, 57 deletions
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 3a68b98ce..89ef16cc9 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,4 +1,4 @@ -import { action, configure } from 'mobx'; +import { action, configure, observable, runInAction } from 'mobx'; import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; @@ -31,6 +31,9 @@ import { faRedoAlt } from '@fortawesome/free-solid-svg-icons'; import { faPenNib } from '@fortawesome/free-solid-svg-icons'; import { faFilm } from '@fortawesome/free-solid-svg-icons'; import { faMusic } from '@fortawesome/free-solid-svg-icons'; +import Measure from 'react-measure'; +import { Field, Opt } from '../../fields/Field'; +import { ListField } from '../../fields/ListField'; configure({ enforceActions: "observed" }); // causes errors to be generated when modifying an observable outside of an action @@ -41,11 +44,17 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { ContextMenu.Instance.clearItems() } }), true) - -const mainDocId = "mainDoc"; -let mainContainer: Document; +const pathname = window.location.pathname.split("/"); +const mainDocId = pathname[pathname.length - 1]; +const pendingDocId = "pending-doc" +var mainContainer: Document; let mainfreeform: Document; +class mainDocFrame { + @observable public static pwidth: number = 0; + @observable public static pheight: number = 0; +} + library.add(faFont); library.add(faImage); library.add(faFilePdf); @@ -73,9 +82,24 @@ Documents.initProtos(mainDocId, (res?: Document) => { var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(mainfreeform)] }] }; mainContainer.SetText(KeyStore.Data, JSON.stringify(dockingLayout)); mainContainer.Set(KeyStore.ActiveFrame, mainfreeform); + let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }, pendingDocId) + mainContainer.Set(KeyStore.OptionalRightCollection, pendingDocument) }, 0); } + // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) + setTimeout(() => { + Server.GetField(pendingDocId, (res?: Field) => { + if (res instanceof Document) { + res.GetTAsync<ListField<Document>>(KeyStore.Data, ListField, (f: Opt<ListField<Document>>) => { + if (f && f.Data.length > 0) { + CollectionDockingView.Instance.AddRightSplit(res) + } + }) + } + }) + }, 50) + let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf" let weburl = "https://cs.brown.edu/courses/cs166/"; @@ -106,16 +130,24 @@ Documents.initProtos(mainDocId, (res?: Document) => { ReactDOM.render(( <div style={{ position: "absolute", width: "100%", height: "100%" }}> {/* <div id="dash-title">— DASH —</div> */} - - <DocumentView Document={mainContainer} - AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity} - ContentScaling={() => 1} - PanelWidth={() => 0} - PanelHeight={() => 0} - isTopMost={true} - SelectOnLoad={false} - focus={() => { }} - ContainingCollectionView={undefined} /> + <Measure onResize={(r: any) => runInAction(() => { + mainDocFrame.pwidth = r.entry.width; + mainDocFrame.pheight = r.entry.height; + })}> + {({ measureRef }) => + <div ref={measureRef} style={{ position: "absolute", width: "100%", height: "100%" }}> + <DocumentView Document={mainContainer} + AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity} + ContentScaling={() => 1} + PanelWidth={() => mainDocFrame.pwidth} + PanelHeight={() => mainDocFrame.pheight} + isTopMost={true} + SelectOnLoad={false} + focus={() => { }} + ContainingCollectionView={undefined} /> + </div> + } + </Measure> <DocumentDecorations /> <ContextMenu /> <button className="clear-db-button" onClick={clearDatabase}>Clear Database</button> diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 94005a4c0..ba9e8c29f 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -212,6 +212,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp stack.remove(); //} })); + stack.header.controlsContainer.find('.lm_popout') //get the close icon + .off('click') //unbind the current click handler + .click(action(function () { + var url = "http://localhost:1050/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId; + let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400"); + })); } render() { diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 5c0eab392..988c9941f 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -1,11 +1,10 @@ -import { action, computed, observable, trace } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; import { TextField } from "../../../fields/TextField"; -import { Documents } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; @@ -198,7 +197,6 @@ export class CollectionFreeFormView extends CollectionViewBase { @action private SetPan(panX: number, panY: number) { var x1 = this.getLocalTransform().inverse().Scale; - var x2 = this.getTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY)); this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX); @@ -316,7 +314,7 @@ export class CollectionFreeFormView extends CollectionViewBase { //when focus is lost, this will remove the preview cursor @action - onBlur = (e: React.FocusEvent<HTMLDivElement>): void => { + onBlur = (): void => { this.PreviewCursorVisible = false; } diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index eda62a4ca..d9598aa72 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -47,6 +47,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps> protected drop(e: Event, de: DragManager.DropEvent) { const docView: DocumentView = de.data["documentView"]; const doc: Document = de.data["document"]; + if (docView && (!docView.props.ContainingCollectionView || docView.props.ContainingCollectionView !== this.props.CollectionView)) { if (docView.props.RemoveDocument) { docView.props.RemoveDocument(docView.props.Document); @@ -61,12 +62,17 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps> @action protected onDrop(e: React.DragEvent, options: DocumentOptions): void { - e.stopPropagation() - e.preventDefault() let that = this; let html = e.dataTransfer.getData("text/html"); let text = e.dataTransfer.getData("text/plain"); + + if (text && text.startsWith("<div")) { + return; + } + e.stopPropagation() + e.preventDefault() + if (html && html.indexOf("<img") != 0) { console.log("not good"); let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 54afda8f3..76127a7f3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -14,20 +14,21 @@ import { Transform } from "../../util/Transform"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; -import {FormattedTextBox} from "../nodes/FormattedTextBox" -import {ImageBox} from "../nodes/ImageBox"; -import {CollectionFreeFormView} from "../collections/CollectionFreeFormView" -import {PDFBox} from "../nodes/PDFBox"; -import {WebBox} from "../nodes/WebBox" -import {CollectionSchemaView} from "../collections/CollectionSchemaView" -import {AudioBox} from "../nodes/AudioBox"; -import {VideoBox} from "../nodes/VideoBox"; -import {CollectionPDFView} from "../collections/CollectionPDFView" -import {CollectionVideoView} from "../collections/CollectionVideoView" -import {KeyValueBox} from "../nodes/KeyValueBox" +import { FormattedTextBox } from "../nodes/FormattedTextBox" +import { ImageBox } from "../nodes/ImageBox"; +import { CollectionFreeFormView } from "../collections/CollectionFreeFormView" +import { PDFBox } from "../nodes/PDFBox"; +import { WebBox } from "../nodes/WebBox" +import { CollectionSchemaView } from "../collections/CollectionSchemaView" +import { AudioBox } from "../nodes/AudioBox"; +import { VideoBox } from "../nodes/VideoBox"; +import { CollectionPDFView } from "../collections/CollectionPDFView" +import { CollectionVideoView } from "../collections/CollectionVideoView" +import { KeyValueBox } from "../nodes/KeyValueBox" import "./DocumentView.scss"; import React = require("react"); import { DocumentContentsView } from "./DocumentContentsView"; +import { Utils } from "../../../Utils"; import { faUserPlus } from "@fortawesome/free-solid-svg-icons"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -243,6 +244,9 @@ export class DocumentView extends React.Component<DocumentViewProps> { SelectionManager.SelectDoc(this, e.ctrlKey); } } + stopPropogation = (e: React.SyntheticEvent) => { + e.stopPropagation(); + } deleteClicked = (): void => { if (this.props.RemoveDocument) { @@ -295,6 +299,20 @@ export class DocumentView extends React.Component<DocumentViewProps> { e.stopPropagation(); } + onDrop = (e: React.DragEvent) => { + if (e.isDefaultPrevented()) { + return; + } + let text = e.dataTransfer.getData("text/plain"); + if (text && text.startsWith("<div")) { + let oldLayout = this.props.Document.GetText(KeyStore.Layout, ""); + let layout = text.replace("{layout}", oldLayout); + this.props.Document.SetText(KeyStore.Layout, layout); + e.stopPropagation(); + e.preventDefault(); + } + } + @action onContextMenu = (e: React.MouseEvent): void => { e.stopPropagation(); @@ -309,6 +327,12 @@ export class DocumentView extends React.Component<DocumentViewProps> { 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 ID", + event: () => { + Utils.CopyText(this.props.Document.Id); + } + }); //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) if (!this.topMost) { @@ -351,6 +375,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { transformOrigin: "left top", transform: `scale(${scaling} , ${scaling})` }} + onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} > <DocumentContents {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} /> diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f5d4c030b..d7026ed67 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -14,6 +14,9 @@ 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"); @@ -60,6 +63,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { 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 }), diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss index e946275fa..63ae75424 100644 --- a/src/client/views/nodes/KeyValueBox.scss +++ b/src/client/views/nodes/KeyValueBox.scss @@ -1,31 +1,57 @@ +@import "../global_variables"; .keyValueBox-cont { - overflow-y: scroll; - height: 100%; - border: black; - border-width: 1px; - border-style: solid; - box-sizing: border-box; - display: inline-block; - .imageBox-cont img { - max-height: 45px; - height: auto; - } + overflow-y: scroll; + height: 100%; + background-color: $light-color; + border: 1px solid $intermediate-color; + border-radius: $border-radius; + box-sizing: border-box; + display: inline-block; + .imageBox-cont img { + max-height: 45px; + height: auto; + } + td { + padding: 6px 8px; + border-right: 1px solid $intermediate-color; + border-top: 1px solid $intermediate-color; + &:last-child { + border-right: none; + } + } } + .keyValueBox-table { - position: relative; + position: relative; + border-collapse: collapse; } + .keyValueBox-header { - background: gray; + background: $intermediate-color; + color: $light-color; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 12px; + height: 30px; + padding-top: 4px; + th { + font-weight: normal; + &:first-child { + border-right: 1px solid $light-color; + } + } } + .keyValueBox-evenRow { - background: white; - .formattedTextBox-cont { - background: white; - } + background: $light-color; + .formattedTextBox-cont { + background: $light-color; + } } + .keyValueBox-oddRow { - background: lightGray; - .formattedTextBox-cont { - background: lightgray; - } -} + background: $light-color-secondary; + .formattedTextBox-cont { + background: $light-color-secondary; + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index ac8c949a9..283c1f732 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -2,17 +2,62 @@ import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { Document } from '../../../fields/Document'; -import { FieldWaiting } from '../../../fields/Field'; +import { FieldWaiting, Field } from '../../../fields/Field'; import { KeyStore } from '../../../fields/KeyStore'; import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; import React = require("react") +import { CompileScript, ToField } from "../../util/Scripting"; +import { Key } from '../../../fields/Key'; +import { observable, action } from "mobx"; @observer export class KeyValueBox extends React.Component<FieldViewProps> { public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr) } + @observable private _keyInput: string = ""; + @observable private _valueInput: string = ""; + + + constructor(props: FieldViewProps) { + super(props); + } + + + + shouldComponentUpdate() { + return false; + } + + @action + onEnterKey = (e: React.KeyboardEvent): void => { + if (e.key == 'Enter') { + if (this._keyInput && this._valueInput) { + let doc = this.props.doc.GetT(KeyStore.Data, Document); + if (!doc || doc == FieldWaiting) { + return + } + let realDoc = doc; + + let script = CompileScript(this._valueInput, undefined, true); + if (!script.compiled) { + return; + } + let field = script(); + if (field instanceof Field) { + realDoc.Set(new Key(this._keyInput), field); + } else { + let dataField = ToField(field); + if (dataField) { + realDoc.Set(new Key(this._keyInput), dataField); + } + } + this._keyInput = "" + this._valueInput = "" + } + } + } onPointerDown = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected()) { @@ -33,7 +78,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { let ids: { [key: string]: string } = {}; let protos = doc.GetAllPrototypes(); for (const proto of protos) { - proto._proxies.forEach((val, key) => { + proto._proxies.forEach((val: any, key: string) => { if (!(key in ids)) { ids[key] = key; } @@ -48,9 +93,26 @@ export class KeyValueBox extends React.Component<FieldViewProps> { return rows; } + @action + keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => { + this._keyInput = e.currentTarget.value; + } + + @action + valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => { + this._valueInput = e.currentTarget.value; + } - render() { + newKeyValue = () => { + return ( + <tr> + <td><input type="text" value={this._keyInput} placeholder="Key" onChange={this.keyChanged} /></td> + <td><input type="text" value={this._valueInput} placeholder="Value" onChange={this.valueChanged} onKeyPress={this.onEnterKey} /></td> + </tr> + ) + } + render() { return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel}> <table className="keyValueBox-table"> <tbody> @@ -59,6 +121,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { <th>Fields</th> </tr> {this.createTable()} + {this.newKeyValue()} </tbody> </table> </div>) diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index a97e98313..111f85a05 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -8,6 +8,8 @@ import { observable, action } from 'mobx'; import { Document } from '../../../fields/Document'; import { Key } from '../../../fields/Key'; import { Server } from "../../Server" +import { EditableView } from "../EditableView"; +import { CompileScript, ToField } from "../../util/Scripting"; // Represents one row in a key value plane @@ -48,10 +50,37 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { bindings: {}, selectOnLoad: false, } + let contents = ( + <FieldView {...props} /> + ); return ( <tr className={this.props.rowStyle}> <td>{this.key.Name}</td> - <td><FieldView {...props} /></td> + <td><EditableView contents={contents} height={36} GetValue={() => { + let field = props.doc.Get(props.fieldKey); + if (field && field instanceof Field) { + return field.ToScriptString(); + } + return field || ""; + }} + SetValue={(value: string) => { + let script = CompileScript(value, undefined, true); + if (!script.compiled) { + return false; + } + let field = script(); + if (field instanceof Field) { + props.doc.Set(props.fieldKey, field); + return true; + } else { + let dataField = ToField(field); + if (dataField) { + props.doc.Set(props.fieldKey, dataField); + return true; + } + } + return false; + }}></EditableView></td> </tr> ) } |
