diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 193 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 336 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 13 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.scss | 9 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 9 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.scss | 3 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/nodes/WebView.tsx | 22 |
8 files changed, 275 insertions, 322 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 565deb145..17123bf52 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,24 +1,16 @@ -import { action, computed } from "mobx"; +import { computed, trace } from "mobx"; import { observer } from "mobx-react"; -import { Key, KeyStore } from "../../../fields/Key"; +import { KeyStore } from "../../../fields/KeyStore"; import { NumberField } from "../../../fields/NumberField"; -import { DragManager } from "../../util/DragManager"; -import { SelectionManager } from "../../util/SelectionManager"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; -import { ContextMenu } from "../ContextMenu"; +import { Transform } from "../../util/Transform"; +import { DocumentView, DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import React = require("react"); -import { DocumentView, DocumentViewProps } from "./DocumentView"; -import { Transform } from "../../util/Transform"; @observer -export class CollectionFreeFormDocumentView extends DocumentView { - private _contextMenuCanOpen = false; - private _downX: number = 0; - private _downY: number = 0; - // private _mainCont = React.createRef<HTMLDivElement>(); +export class CollectionFreeFormDocumentView extends React.Component<DocumentViewProps> { + private _mainCont = React.createRef<HTMLDivElement>(); constructor(props: DocumentViewProps) { super(props); @@ -30,17 +22,9 @@ export class CollectionFreeFormDocumentView extends DocumentView { return new DOMRect(); } - public LeftCorner(): number { - return this.props.Document.GetNumber(KeyStore.X, 0) + super.LeftCorner(); - } - - public TopCorner(): number { - return this.props.Document.GetNumber(KeyStore.Y, 0) + super.TopCorner(); - } - @computed get transform(): string { - return `scale(${this.props.ParentScaling}, ${this.props.ParentScaling}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px)`; + return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px)`; } @computed @@ -85,158 +69,35 @@ export class CollectionFreeFormDocumentView extends DocumentView { this.props.Document.SetData(KeyStore.ZIndex, h, NumberField) } - @action - dragComplete = (e: DragManager.DragCompleteEvent) => { + contentScaling = () => { + return this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; } - @computed - get active(): boolean { - return SelectionManager.IsSelected(this) || this.props.ContainingCollectionView === undefined || - this.props.ContainingCollectionView.active; + getTransform = (): Transform => { + return this.props.ScreenToLocalTransform(). + translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0)).scale(1 / this.contentScaling()); } @computed - get topMost(): boolean { - return this.props.ContainingCollectionView == undefined || this.props.ContainingCollectionView instanceof CollectionDockingView; - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - var me = this; - if (e.shiftKey && e.buttons === 1) { - CollectionDockingView.StartOtherDrag(this._mainCont.current!, this.props.Document); - e.stopPropagation(); - return; - } - this._contextMenuCanOpen = e.button == 2; - if (this.active && !e.isDefaultPrevented()) { - e.stopPropagation(); - if (e.buttons === 2) { - e.preventDefault(); - } - document.removeEventListener("pointermove", this.onPointerMove) - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp) - document.addEventListener("pointerup", this.onPointerUp); - } - - } - - onPointerMove = (e: PointerEvent): void => { - if (e.cancelBubble) { - this._contextMenuCanOpen = false; - return; - } - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - this._contextMenuCanOpen = false; - if (this._mainCont.current != null && !this.topMost) { - this._contextMenuCanOpen = false; - const rect = this.screenRect; - let dragData: { [id: string]: any } = {}; - dragData["document"] = this; - dragData["xOffset"] = e.x - rect.left; - dragData["yOffset"] = e.y - rect.top; - DragManager.StartDrag(this._mainCont.current, dragData, { - - handlers: { - dragComplete: this.dragComplete, - }, - hideSource: true - - }) - //remove preview cursor from collection - if (this.props.ContainingCollectionView != undefined && this.props.ContainingCollectionView instanceof CollectionFreeFormView) { - this.props.ContainingCollectionView.hidePreviewCursor(); - } - } - } - e.stopPropagation(); - e.preventDefault(); - } - - onPointerUp = (e: PointerEvent): void => { - 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) { - SelectionManager.SelectDoc(this, e.ctrlKey); - } - } - - openRight = (e: React.MouseEvent): void => { - CollectionDockingView.AddRightSplit(this.props.Document); - } - - deleteClicked = (e: React.MouseEvent): void => { - if (this.props.ContainingCollectionView instanceof CollectionFreeFormView) { - this.props.ContainingCollectionView.removeDocument(this.props.Document) - } - } - @action - fullScreenClicked = (e: React.MouseEvent): void => { - CollectionDockingView.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) - } - @action - closeFullScreenClicked = (e: React.MouseEvent): void => { - CollectionDockingView.CloseFullScreen(); - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } - - @action - onContextMenu = (e: React.MouseEvent): void => { - if (!SelectionManager.IsSelected(this)) { - return; - } - e.preventDefault() - - if (!this._contextMenuCanOpen) { - return; - } - - if (this.topMost) { - ContextMenu.Instance.clearItems() - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } - else { - // DocumentViews should stop propogation of this event - e.stopPropagation(); - - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight }) - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - SelectionManager.SelectDoc(this, e.ctrlKey); - } - } - - getTransform = (): Transform => { - return this.props.GetTransform().translated(this.props.Document.GetNumber(KeyStore.X, 0), this.props.Document.GetNumber(KeyStore.Y, 0)); + get docView() { + return <DocumentView {...this.props} + ContentScaling={this.contentScaling} + ScreenToLocalTransform={this.getTransform} + /> } render() { - var parentScaling = this.nativeWidth > 0 ? this.width / this.nativeWidth : 1; return ( - <div className="documentView-node" ref={this._mainCont} - onContextMenu={this.onContextMenu} - onPointerDown={this.onPointerDown} - style={{ - transformOrigin: "left top", - transform: this.transform, - width: this.width, - height: this.height, - position: "absolute", - zIndex: this.zIndex, - }}> - - <DocumentView {...this.props} ref={this._renderDoc} ParentScaling={parentScaling} GetTransform={this.getTransform} DocumentView={this} /> + <div ref={this._mainCont} style={{ + transformOrigin: "left top", + transform: this.transform, + width: this.width, + height: this.height, + position: "absolute", + zIndex: this.zIndex, + backgroundColor: "transparent" + }} > + {this.docView} </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c5270e0cd..914182efa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,192 +1,248 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Opt, FieldWaiting } from "../../../fields/Field"; -import { Key, KeyStore } from "../../../fields/Key"; +import { Field, FieldWaiting, Opt } from "../../../fields/Field"; +import { Key } from "../../../fields/Key"; +import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { Utils } from "../../../Utils"; +import { DragManager } from "../../util/DragManager"; +import { SelectionManager } from "../../util/SelectionManager"; +import { Transform } from "../../util/Transform"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "../collections/CollectionViewBase"; +import { CollectionView, CollectionViewType } from "../collections/CollectionView"; +import { WebView } from "./WebView"; +import { ContextMenu } from "../ContextMenu"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import "./DocumentView.scss"; import React = require("react"); -import { Transform } from "../../util/Transform"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? export interface DocumentViewProps { - DocumentView: Opt<DocumentView> // needed only to set ContainingDocumentView on CollectionViewProps when invoked from JsxParser -- is there a better way? - ContainingCollectionView: Opt<CollectionViewBase>; + ContainingCollectionView: Opt<CollectionView>; Document: Document; AddDocument?: (doc: Document) => void; RemoveDocument?: (doc: Document) => boolean; - GetTransform: () => Transform; - ParentScaling: number; + ScreenToLocalTransform: () => Transform; + isTopMost: boolean; + //tfs: This shouldn't be necessary I don't think + ContentScaling: () => number; + PanelWidth: () => number; + PanelHeight: () => number; +} +export interface JsxArgs extends DocumentViewProps { + Keys: { [name: string]: Key } + Fields: { [name: string]: Field } } -@observer -export class DocumentView extends React.Component<DocumentViewProps> { - - protected _renderDoc = React.createRef<any>(); - protected _mainCont = React.createRef<any>(); - get MainContent() { - return this._mainCont; - } - - @computed - get parentScaling(): number { - return this._renderDoc.current ? this._renderDoc.current.props.ParentScaling : this.props.ParentScaling > 0 ? this.props.ParentScaling : 1; +/* +This function is pretty much a hack that lets us fill out the fields in JsxArgs with something that +jsx-to-string can recover the jsx from +Example usage of this function: + public static LayoutString() { + let args = FakeJsxArgs(["Data"]); + return jsxToString( + <CollectionFreeFormView + doc={args.Document} + fieldKey={args.Keys.Data} + DocumentViewForField={args.DocumentView} />, + { useFunctionCode: true, functionNameOnly: true } + ) } - - @computed - get layout(): string { - return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); +*/ +export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs { + let Keys: { [name: string]: any } = {} + let Fields: { [name: string]: any } = {} + for (const key of keys) { + let fn = () => { } + Object.defineProperty(fn, "name", { value: key + "Key" }) + Keys[key] = fn; } - - @computed - get backgroundLayout(): string { - return this.props.Document.GetText(KeyStore.BackgroundLayout, ""); + for (const field of fields) { + let fn = () => { } + Object.defineProperty(fn, "name", { value: field }) + Fields[field] = fn; } + let args: JsxArgs = { + Document: function Document() { }, + DocumentView: function DocumentView() { }, + Keys, + Fields + } as any; + return args; +} - @computed - get layoutKeys(): Key[] { - return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); - } +@observer +export class DocumentView extends React.Component<DocumentViewProps> { - @computed - get layoutFields(): Key[] { - return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); + private _mainCont = React.createRef<HTMLDivElement>(); + private _documentBindings: any = null; + private _contextMenuCanOpen = false; + private _downX: number = 0; + private _downY: number = 0; + + @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 === 1) { + CollectionDockingView.Instance.StartOtherDrag(this._mainCont.current!, this.props.Document); + e.stopPropagation(); + } else { + this._contextMenuCanOpen = true; + if (this.active && !e.isDefaultPrevented()) { + e.stopPropagation(); + if (e.buttons === 2) { + e.preventDefault(); + } + document.removeEventListener("pointermove", this.onPointerMove) + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp) + document.addEventListener("pointerup", this.onPointerUp); + } + } } - // - // returns the cumulative scaling between the document and the screen - // - @computed - public get ScalingToScreenSpace(): number { - if (this.props.ContainingCollectionView != undefined && - this.props.ContainingCollectionView.props.ContainingDocumentView != undefined) { - let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1); - return this.props.ContainingCollectionView.props.ContainingDocumentView.ScalingToScreenSpace * ss; + onPointerMove = (e: PointerEvent): void => { + if (e.cancelBubble) { + this._contextMenuCanOpen = false; + return; + } + if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { + //remove preview cursor from collection + if (this.props.ContainingCollectionView != undefined && this.props.ContainingCollectionView instanceof CollectionFreeFormView) { + this.props.ContainingCollectionView.hidePreviewCursor(); + } + this._contextMenuCanOpen = false; + if (this._mainCont.current != null && !this.topMost) { + this._contextMenuCanOpen = false; + const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + let dragData: { [id: string]: any } = {}; + dragData["document"] = this; + dragData["xOffset"] = e.x - left; + dragData["yOffset"] = e.y - top; + DragManager.StartDrag(this._mainCont.current, dragData, { + handlers: { + dragComplete: action((e: DragManager.DragCompleteEvent) => { }), + }, + hideSource: true + }) + } } - return 1; + e.stopPropagation(); + e.preventDefault(); } - - public LeftCorner(): number { - if (this.props.ContainingCollectionView) { - if (this.props.ContainingCollectionView instanceof CollectionDockingView) { - // this is a hacky way to account for the titles/pane placement/etc of a CollectionDockingView - // this only works if the collectionDockingView is the root collection, too. - // need to find a better way. - var { translateX: rx, translateY: ry } = Utils.GetScreenTransform(this.MainContent.current!); - return rx + COLLECTION_BORDER_WIDTH; - } - return COLLECTION_BORDER_WIDTH; // assumes all collections have the same border + onPointerUp = (e: PointerEvent): void => { + 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) { + SelectionManager.SelectDoc(this, e.ctrlKey); } - return 0; } - public TopCorner(): number { - if (this.props.ContainingCollectionView) { - if (this.props.ContainingCollectionView instanceof CollectionDockingView) { - // this is a hacky way to account for the titles/pane placement/etc of a CollectionDockingView - // this only works if the collectionDockingView is the root collection, too. - // need to find a better way. - var { translateX: rx, translateY: ry } = Utils.GetScreenTransform(this.MainContent.current!); - return ry + COLLECTION_BORDER_WIDTH; - } - return COLLECTION_BORDER_WIDTH; // assumes all collections have the same border + deleteClicked = (e: React.MouseEvent): void => { + if (this.props.RemoveDocument) { + this.props.RemoveDocument(this.props.Document); } - return 0; } - // - // Converts a coordinate in the screen space of the app to a local point in the space of the DocumentView. - // This also returns the point in the coordinate space of this document's containing CollectionView - // - public TransformToLocalPoint(screenX: number, screenY: number) { - // if this collection view is nested within another collection view, then - // first transform the screen point into the parent collection's coordinate space. - let containingCollectionViewDoc = this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined; - let { LocalX: parentX, LocalY: parentY } = !containingCollectionViewDoc ? { LocalX: screenX, LocalY: screenY } : - containingCollectionViewDoc.TransformToLocalPoint(screenX, screenY); - let ContainerX: number = parentX - COLLECTION_BORDER_WIDTH; - let ContainerY: number = parentY - COLLECTION_BORDER_WIDTH; - - let Ss = this.props.Document.GetNumber(KeyStore.Scale, 1); - let Panxx = this.props.Document.GetNumber(KeyStore.PanX, 0); - let Panyy = this.props.Document.GetNumber(KeyStore.PanY, 0); - let LocalX = (ContainerX - (this.LeftCorner() + Panxx)) / Ss; - let LocalY = (ContainerY - (this.TopCorner() + Panyy)) / Ss; - - return { LocalX, LocalY, ContainerX, ContainerY }; + 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(); + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) } - // - // Converts a point in the coordinate space of the document to coordinate in app screen coordinates - // - public TransformToScreenPoint(localX: number, localY: number, applyViewXf: boolean = false): { ScreenX: number, ScreenY: number } { - var parentScaling = applyViewXf ? this.parentScaling : 1; - let Panxx = applyViewXf ? this.props.Document.GetNumber(KeyStore.PanX, 0) : 0; - let Panyy = applyViewXf ? this.props.Document.GetNumber(KeyStore.PanY, 0) : 0; - var Zoom = applyViewXf ? this.props.Document.GetNumber(KeyStore.Scale, 1) : 1; - - let parentX = this.LeftCorner() + (Panxx + (localX - COLLECTION_BORDER_WIDTH) * Zoom) * parentScaling; - let parentY = this.TopCorner() + (Panyy + (localY - COLLECTION_BORDER_WIDTH) * Zoom) * parentScaling; - // if this collection view is nested within another collection view, then - // first transform the local point into the parent collection's coordinate space. - let containingDocView = this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined; - if (containingDocView) { - let { ScreenX, ScreenY } = containingDocView.TransformToScreenPoint(parentX + COLLECTION_BORDER_WIDTH * parentScaling, parentY + COLLECTION_BORDER_WIDTH * parentScaling, true); - parentX = ScreenX; - parentY = ScreenY; + @action + onContextMenu = (e: React.MouseEvent): void => { + e.preventDefault() + e.stopPropagation(); + if (!SelectionManager.IsSelected(this) || !this._contextMenuCanOpen) { + return; + } + + if (this.topMost) { + ContextMenu.Instance.clearItems() + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + } + else { + // DocumentViews should stop propagation of this event + e.stopPropagation(); + + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }) + ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) + 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) }) + ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + SelectionManager.SelectDoc(this, e.ctrlKey); } - return { ScreenX: parentX, ScreenY: parentY }; } + @computed get mainContent() { + return <JsxParser + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }} + bindings={this._documentBindings} + jsx={this.layout} + showWarnings={true} + onError={(test: any) => { console.log(test) }} + /> + } render() { - let bindings = { ...this.props } as any; + if (!this.props.Document) + return <div></div> + let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField); + if (!lkeys || lkeys === "<Waiting>") { + return <p>Error loading layout keys</p>; + } + this._documentBindings = { + ...this.props, + isSelected: () => SelectionManager.IsSelected(this), + select: (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed) + }; for (const key of this.layoutKeys) { - bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data + this._documentBindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data } for (const key of this.layoutFields) { let field = this.props.Document.Get(key); - bindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field; - } - if (bindings.DocumentView === undefined) { - bindings.DocumentView = this; // set the DocumentView to this if it hasn't already been set by a sub-class during its render method. + this._documentBindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field; } - if (this.backgroundLayout) { - var backgroundview = <JsxParser - components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} - bindings={bindings} - jsx={this.backgroundLayout} - showWarnings={true} - onError={(test: any) => { console.log(test) }} - />; - bindings["BackgroundView"] = backgroundview; - } - var nativewidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); - var nativeheight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0); - var width = nativewidth > 0 ? nativewidth + "px" : "100%"; - var height = nativeheight > 0 ? nativeheight + "px" : "100%"; + this._documentBindings.bindings = this._documentBindings; + + var scaling = this.props.ContentScaling(); + var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); + var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0); return ( <div className="documentView-node" ref={this._mainCont} style={{ - width: width, - height: height, - transformOrigin: "top left", - transform: `scale(${this.props.ParentScaling},${this.props.ParentScaling})` - }}> - <JsxParser - components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} - bindings={bindings} - jsx={this.layout} - showWarnings={true} - onError={(test: any) => { console.log(test) }} - /> + width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%", + height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%", + transformOrigin: "left top", + transform: `scale(${scaling},${scaling})` + }} + onContextMenu={this.onContextMenu} + onPointerDown={this.onPointerDown} > + {this.mainContent} </div> ) } diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 12371eb2e..1a9d325db 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -10,7 +10,8 @@ import { ImageField } from "../../../fields/ImageField"; import { Key } from "../../../fields/Key"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; -import { DocumentView } from "./DocumentView"; +import { HtmlField } from "../../../fields/HtmlField"; +import { WebView } from "./WebView"; // // these properties get assigned through the render() method of the DocumentView when it creates this node. @@ -20,12 +21,16 @@ import { DocumentView } from "./DocumentView"; export interface FieldViewProps { fieldKey: Key; doc: Document; - DocumentViewForField: Opt<DocumentView> + isSelected: () => boolean; + select: () => void; + isTopMost: boolean; + bindings: any; } @observer export class FieldView extends React.Component<FieldViewProps> { - public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; } + public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={${fieldStr}} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; } + @computed get field(): FieldValue<Field> { const { doc, fieldKey } = this.props; @@ -47,6 +52,8 @@ export class FieldView extends React.Component<FieldViewProps> { } else if (field instanceof NumberField) { return <p>{field.Data}</p> + } else if (field instanceof HtmlField) { + return <WebView {...this.props} /> } else if (field != FieldWaiting) { return <p>{field.GetValue}</p> } else diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 492367fce..12604f843 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -1,7 +1,7 @@ .ProseMirror { - margin-top: -1em; width: 100%; - height: 100%; + height: auto; + min-height: 100% } .ProseMirror:focus { @@ -9,6 +9,11 @@ } .formattedTextBox-cont { +<<<<<<< HEAD background: white; padding: 1vw; +======= + background: beige; + padding: 0; +>>>>>>> d9d55e422826da1fe87aa7973c92e54bc0c99f02 }
\ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 885cb55c4..194d5636c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -8,17 +8,19 @@ import { EditorState, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Node } from "prosemirror-model"; import { Opt, FieldWaiting, FieldValue } from "../../../fields/Field"; -import { SelectionManager } from "../../util/SelectionManager"; import "./FormattedTextBox.scss"; import { KeyStore } from "../../../fields/Key"; import React = require("react") import { RichTextField } from "../../../fields/RichTextField"; import { FieldViewProps, FieldView } from "./FieldView"; +<<<<<<< HEAD import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; import { observer } from "mobx-react"; import { Schema, DOMParser } from "prosemirror-model" import { Plugin } from 'prosemirror-state' import { Decoration, DecorationSet } from 'prosemirror-view' +======= +>>>>>>> d9d55e422826da1fe87aa7973c92e54bc0c99f02 // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document @@ -39,7 +41,7 @@ import { Decoration, DecorationSet } from 'prosemirror-view' //] export class FormattedTextBox extends React.Component<FieldViewProps> { - public static LayoutString() { return FieldView.LayoutString("FormattedTextBox"); } + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) } private _ref: React.RefObject<HTMLDivElement>; private _editorView: Opt<EditorView>; private _reactionDisposer: Opt<IReactionDisposer>; @@ -137,7 +139,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { } onPointerDown = (e: React.PointerEvent): void => { let me = this; - if (e.buttons === 1 && me.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(me.props.DocumentViewForField)) { + if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); } } @@ -149,6 +151,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { style={{ color: "initial", whiteSpace: "initial", + height: "auto" }} onPointerDown={this.onPointerDown} ref={this._ref} />) diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 36f5e0fe0..5b15b3329 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -1,7 +1,8 @@ .imageBox-cont { padding: 0vw; - position: absolute; + position: relative; + text-align: center; width: 100%; max-width: 100%; max-height: 100% diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2fa70734d..b5ce8b28c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,21 +1,19 @@ import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { SelectionManager } from "../../util/SelectionManager"; import "./ImageBox.scss"; import React = require("react") import { ImageField } from '../../../fields/ImageField'; import { FieldViewProps, FieldView } from './FieldView'; -import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView'; import { FieldWaiting } from '../../../fields/Field'; import { observer } from "mobx-react" -import { observable, action, spy } from 'mobx'; -import { KeyStore } from '../../../fields/Key'; +import { observable, action } from 'mobx'; +import { KeyStore } from '../../../fields/KeyStore'; @observer export class ImageBox extends React.Component<FieldViewProps> { - public static LayoutString() { return FieldView.LayoutString("ImageBox"); } + public static LayoutString() { return FieldView.LayoutString(ImageBox) } private _ref: React.RefObject<HTMLDivElement>; private _downX: number = 0; private _downY: number = 0; @@ -41,7 +39,7 @@ export class ImageBox extends React.Component<FieldViewProps> { onPointerDown = (e: React.PointerEvent): void => { if (Date.now() - this._lastTap < 300) { - if (e.buttons === 1 && this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)) { + if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); this._downX = e.clientX; this._downY = e.clientY; @@ -63,7 +61,7 @@ export class ImageBox extends React.Component<FieldViewProps> { lightbox = (path: string) => { const images = [path, "http://www.cs.brown.edu/~bcz/face.gif"]; - if (this._isOpen && this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)) { + if (this._isOpen && this.props.isSelected()) { return (<Lightbox mainSrc={images[this._photoIndex]} nextSrc={images[(this._photoIndex + 1) % images.length]} diff --git a/src/client/views/nodes/WebView.tsx b/src/client/views/nodes/WebView.tsx new file mode 100644 index 000000000..717aa8bf5 --- /dev/null +++ b/src/client/views/nodes/WebView.tsx @@ -0,0 +1,22 @@ +import { FieldViewProps, FieldView } from "./FieldView"; +import { computed } from "mobx"; +import { observer } from "mobx-react"; +import { KeyStore } from "../../../fields/KeyStore"; +import React = require('react') +import { TextField } from "../../../fields/TextField"; +import { HtmlField } from "../../../fields/HtmlField"; +import { RichTextField } from "../../../fields/RichTextField"; + +@observer +export class WebView extends React.Component<FieldViewProps> { + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(WebView, fieldStr) } + + @computed + get html(): string { + return this.props.doc.GetData(KeyStore.Data, HtmlField, "" as string); + } + + render() { + return <span dangerouslySetInnerHTML={{ __html: this.html }}></span> + } +}
\ No newline at end of file |
