diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 16 | ||||
-rw-r--r-- | src/client/views/collections/CollectionFreeFormView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/nodes/KeyValueBox.scss | 24 | ||||
-rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 89 | ||||
-rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 55 | ||||
-rw-r--r-- | src/fields/KVPField | 0 | ||||
-rw-r--r-- | src/fields/KVPField.ts | 30 |
9 files changed, 232 insertions, 4 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ba13cc31b..1d24ff7d2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,6 +13,8 @@ import { CollectionView, CollectionViewType } from "../views/collections/Collect import { HtmlField } from "../../fields/HtmlField"; import { Key } from "../../fields/Key" import { Field } from "../../fields/Field"; +import { KeyValueBox } from "../views/nodes/KeyValueBox" +import { KVPField } from "../../fields/KVPField"; export interface DocumentOptions { x?: number; @@ -35,10 +37,12 @@ export namespace Documents { let imageProto: Document; let webProto: Document; let collProto: Document; + let kvpProto: Document; const textProtoId = "textProto"; const imageProtoId = "imageProto"; const webProtoId = "webProto"; const collProtoId = "collectionProto"; + const kvpProtoId = "kvpProto"; export function initProtos(mainDocId: string, callback: (mainDoc?: Document) => void) { Server.GetFields([collProtoId, textProtoId, imageProtoId, mainDocId], (fields) => { @@ -46,6 +50,7 @@ export namespace Documents { imageProto = fields[imageProtoId] as Document; textProto = fields[textProtoId] as Document; webProto = fields[webProtoId] as Document; + kvpProto = fields[kvpProtoId] as Document; callback(fields[mainDocId] as Document) }); } @@ -98,6 +103,12 @@ export namespace Documents { { panx: 0, pany: 0, scale: 1, layoutKeys: [KeyStore.Data] }); } + function GetKVPPrototype(): Document { + return kvpProto ? kvpProto : + kvpProto = setupPrototypeOptions(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(), + { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] }) + } + export function ImageDocument(url: string, options: DocumentOptions = {}) { let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }, new URL(url), ImageField); @@ -124,6 +135,11 @@ export namespace Documents { export function DockDocument(config: string, options: DocumentOptions, id?: string) { return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, config, TextField, id) } + export function KVPDocument(document: Document, options: DocumentOptions = {}, id?: string) { + var deleg = GetKVPPrototype().MakeDelegate(id); + deleg.Set(KeyStore.Data, document); + return assignOptions(deleg, options); + } diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 63255dd90..96b6d1477 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -16,6 +16,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import { WebBox } from "../nodes/WebBox"; +import { KeyValueBox } from "../nodes/KeyValueBox" import "./CollectionFreeFormView.scss"; import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { CollectionViewBase } from "./CollectionViewBase"; @@ -194,7 +195,6 @@ export class CollectionFreeFormView extends CollectionViewBase { }); } - @computed get backgroundLayout(): string | undefined { let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField); if (field && field !== "<Waiting>") { @@ -231,7 +231,7 @@ export class CollectionFreeFormView extends CollectionViewBase { get backgroundView() { return !this.backgroundLayout ? (null) : (<JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox }} bindings={this.props.bindings} jsx={this.backgroundLayout} showWarnings={true} @@ -242,7 +242,7 @@ export class CollectionFreeFormView extends CollectionViewBase { get overlayView() { return !this.overlayLayout ? (null) : (<JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox }} bindings={this.props.bindings} jsx={this.overlayLayout} showWarnings={true} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8332249c9..f938d2237 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -49,6 +49,7 @@ export class CollectionView extends React.Component<CollectionViewProps> { } } + @action removeDocument = (doc: Document): boolean => { //TODO This won't create the field if it doesn't already exist @@ -60,6 +61,7 @@ export class CollectionView extends React.Component<CollectionViewProps> { break; } } + if (index !== -1) { value.splice(index, 1) diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d5b4a723d..a02fde133 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -15,11 +15,15 @@ import { CollectionView, CollectionViewType } from "../collections/CollectionVie import { ContextMenu } from "../ContextMenu"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; +import { Documents } from "../../documents/Documents" +import { KeyValueBox } from "./KeyValueBox" import { WebBox } from "../nodes/WebBox"; import "./DocumentView.scss"; import React = require("react"); +import { CollectionViewProps } from "../collections/CollectionViewBase"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? + export interface DocumentViewProps { ContainingCollectionView: Opt<CollectionView>; @@ -151,12 +155,19 @@ export class DocumentView extends React.Component<DocumentViewProps> { this.props.RemoveDocument(this.props.Document); } } + + fieldsClicked = (e: React.MouseEvent): void => { + if (this.props.AddDocument) { + this.props.AddDocument(Documents.KVPDocument(this.props.Document)); + } + } 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) } + closeFullScreenClicked = (e: React.MouseEvent): void => { CollectionDockingView.Instance.CloseFullScreen(); ContextMenu.Instance.clearItems(); @@ -175,6 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { e.preventDefault() ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked }) ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }) ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) }) ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) }) @@ -193,7 +205,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { @computed get mainContent() { return <JsxParser - components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox }} bindings={this._documentBindings} jsx={this.layout} showWarnings={true} diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss new file mode 100644 index 000000000..f22989735 --- /dev/null +++ b/src/client/views/nodes/KeyValueBox.scss @@ -0,0 +1,24 @@ +.keyValueBox-cont { + overflow-y:scroll; + height: 100%; + border: black; + border-width: 1px; + border-style: solid; + box-sizing: border-box; + display: inline-block; +} +.keyValueBox-header { + background:gray; +} +.keyValueBox-evenRow { + background: white; + .formattedTextBox-cont { + background: white; + } +} +.keyValueBox-oddRow { + background: lightGray; + .formattedTextBox-cont { + background: lightgray; + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx new file mode 100644 index 000000000..7e2426b58 --- /dev/null +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -0,0 +1,89 @@ + +import { IReactionDisposer } from 'mobx'; +import { observer } from "mobx-react"; +import { EditorView } from 'prosemirror-view'; +import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app +import { Document } from '../../../fields/Document'; +import { Opt } from '../../../fields/Field'; +import { KeyStore } from '../../../fields/KeyStore'; +import { FieldView, FieldViewProps } from './FieldView'; +import { KeyValuePair } from "./KeyValuePair"; +import "./KeyValueBox.scss"; +import React = require("react") + +@observer +export class KeyValueBox extends React.Component<FieldViewProps> { + + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr) } + private _ref: React.RefObject<HTMLDivElement>; + private _editorView: Opt<EditorView>; + private _reactionDisposer: Opt<IReactionDisposer>; + + + constructor(props: FieldViewProps) { + super(props); + + this._ref = React.createRef(); + } + + + + shouldComponentUpdate() { + return false; + } + + + onPointerDown = (e: React.PointerEvent): void => { + if (e.buttons === 1 && this.props.isSelected()) { + e.stopPropagation(); + } + } + onPointerWheel = (e: React.WheelEvent): void => { + e.stopPropagation(); + } + + createTable = () => { + let table: Array<JSX.Element> = [] + let ret = "waiting" + let doc = this.props.doc.GetT(KeyStore.Data, Document); + if (!doc || doc == "<Waiting>") { + return <tr><td>Loading...</td></tr> + } + let realDoc = doc; + + let ids: { [key: string]: string } = {}; + let protos = doc.GetAllPrototypes(); + for (const proto of protos) { + proto._proxies.forEach((val, key) => { + if (!(key in ids)) { + ids[key] = key; + } + }) + } + + let rows: JSX.Element[] = []; + let i = 0; + for (let key in ids) { + if (i++ % 2 == 0) + rows.push(<KeyValuePair doc={realDoc} rowStyle="keyValueBox-evenRow" fieldId={key} />) + else rows.push(<KeyValuePair doc={realDoc} rowStyle="keyValueBox-oddRow" fieldId={key} />) + } + return rows; + } + + + render() { + + return (<div className="keyValueBox-cont"> + <table className="keyValueBox-table"> + <tbody> + <tr className="keyValueBox-header"> + <th>Key</th> + <th>Fields</th> + </tr> + {this.createTable()} + </tbody> + </table> + </div>) + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx new file mode 100644 index 000000000..ecdd47a1e --- /dev/null +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -0,0 +1,55 @@ +import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app +import "./KeyValueBox.scss"; +import React = require("react") +import { FieldViewProps, FieldView } from './FieldView'; +import { Opt, Field } from '../../../fields/Field'; +import { observer } from "mobx-react" +import { observable, action } from 'mobx'; +import { Document } from '../../../fields/Document'; +import { Key } from '../../../fields/Key'; +import { Server } from "../../Server" + +// Represents one row in a key value plane + +export interface KeyValuePairProps { + rowStyle: string; + fieldId: string; + doc: Document; +} +@observer +export class KeyValuePair extends React.Component<KeyValuePairProps> { + + @observable + private key: Opt<Key> + + constructor(props: KeyValuePairProps) { + super(props); + Server.GetField(this.props.fieldId, + action((field: Opt<Field>) => { + if (field) { + this.key = field as Key; + } + })); + + } + + + render() { + if (!this.key) { + return <tr><td>error</td><td></td></tr> + + } + let props: FieldViewProps = { + doc: this.props.doc, + fieldKey: this.key, + isSelected: () => false, + select: () => { }, + isTopMost: false, + bindings: {}, + selectOnLoad: false, + } + return ( + <tr className={this.props.rowStyle}><td>{this.key.Name}</td><td><FieldView {...props} /></td></tr> + ) + } +}
\ No newline at end of file diff --git a/src/fields/KVPField b/src/fields/KVPField new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/fields/KVPField diff --git a/src/fields/KVPField.ts b/src/fields/KVPField.ts new file mode 100644 index 000000000..a7ecc0768 --- /dev/null +++ b/src/fields/KVPField.ts @@ -0,0 +1,30 @@ +import { BasicField } from "./BasicField" +import { FieldId } from "./Field"; +import { Types } from "../server/Message"; +import { Document } from "./Document" + +export class KVPField extends BasicField<Document> { + constructor(data: Document | undefined = undefined, id?: FieldId, save: boolean = true) { + super(data == undefined ? new Document() : data, save, id); + } + + toString(): string { + return this.Data.Title; + } + + ToScriptString(): string { + return `new KVPField("${this.Data}")`; + } + + Copy() { + return new KVPField(this.Data); + } + + ToJson(): { type: Types, data: Document, _id: string } { + return { + type: Types.Text, + data: this.Data, + _id: this.Id + } + } +}
\ No newline at end of file |