From 6d036e9d0a4b6ecbcf63404941e9b192135ffa41 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 19 Feb 2019 09:29:34 -0500 Subject: converted docking collection to use a GoldenLayout layout string for its data --- src/client/documents/Documents.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 72fa608ad..36119e552 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -169,6 +169,7 @@ export namespace Documents { export function CollectionDocument(documents: Array, options: DocumentOptions = {}): Document { let doc = GetCollectionPrototype().MakeDelegate(); + Server.AddDocument(doc); setupOptions(doc, options); doc.Set(KeyStore.Data, new ListField(documents)); return doc; -- cgit v1.2.3-70-g09d2 From 49e946d57c5dc0f21a6d6a98436429a353c96907 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 19 Feb 2019 11:13:22 -0500 Subject: added layout saving/loading to collectiondockingview & cleaned up to use react-component --- src/client/documents/Documents.ts | 4 +- src/client/views/Main.tsx | 82 +++--- .../views/collections/CollectionDockingView.tsx | 303 +++++++++------------ src/client/views/nodes/DocumentView.tsx | 8 +- 4 files changed, 174 insertions(+), 223 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 36119e552..f954b37a8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -107,10 +107,10 @@ export namespace Documents { return dockProto; } - export function DockDocument(documents: Array, options: DocumentOptions = {}): Document { + export function DockDocument(config: string, options: DocumentOptions = {}): Document { let doc = GetDockPrototype().MakeDelegate(); setupOptions(doc, options); - doc.Set(KeyStore.Data, new ListField(documents)); + doc.SetText(KeyStore.Data, config); return doc; } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 933c904fb..a86fdb9b0 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -14,6 +14,7 @@ import { ContextMenu } from './ContextMenu'; import { DocumentView } from './nodes/DocumentView'; import { ImageField } from '../../fields/ImageField'; import { Transform } from '../util/Transform'; +import { CollectionDockingView } from './collections/CollectionDockingView'; configure({ @@ -21,9 +22,6 @@ configure({ }); const mainNodeCollection = new Array(); -let mainContainer = Documents.DockDocument(mainNodeCollection, { - x: 0, y: 0, title: "main container" -}) window.addEventListener("drop", function (e) { e.preventDefault(); @@ -38,49 +36,41 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { }), true) -//runInAction(() => -{ - let doc1 = Documents.TextDocument({ title: "hello", width: 400, height: 300 }); - let doc2 = doc1.MakeDelegate(); - doc2.Set(KS.X, new NumberField(150)); - doc2.Set(KS.Y, new NumberField(20)); - let doc3 = Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", { - x: 450, y: 100, title: "dog", width: 606, height: 386, nativeWidth: 606, nativeHeight: 386 - }); - //doc3.Set(KeyStore.Data, new ImageField); - const schemaDocs = Array.from(Array(5).keys()).map(v => Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", { - x: 50 + 100 * v, y: 50, width: 100, height: 100, title: "cat" + v, nativeWidth: 606, nativeHeight: 386 - })); - schemaDocs.push(doc3); - schemaDocs[0].SetData(KS.Author, "Tyler", TextField); - schemaDocs[4].SetData(KS.Author, "Bob", TextField); - schemaDocs.push(doc2); - const doc7 = Documents.SchemaDocument(schemaDocs) - const docset = [doc1, doc2, doc3, doc7]; - let doc4 = Documents.CollectionDocument(docset, { - x: 0, y: 400, title: "mini collection" - }); - // let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { - // x: 650, y: 500, width: 600, height: 600, title: "cat 2" - // }); - let docset2 = [doc3, doc4, doc2]; - let doc6 = Documents.CollectionDocument(docset2, { - x: 350, y: 100, width: 600, height: 600, title: "docking collection" - }); - let mainNodes = mainContainer.GetOrCreate>(KeyStore.Data, ListField); - // mainNodes.Data.push(doc6); - // mainNodes.Data.push(doc2); - mainNodes.Data.push(doc4); - mainNodes.Data.push(doc3); - // mainNodes.Data.push(doc5); - // mainNodes.Data.push(doc1); - // mainNodes.Data.push(doc2); - mainNodes.Data.push(doc6); - var layoutstr = `{"settings":{"hasHeaders":true,"constrainDragToContainer":true,"reorderEnabled":true,"selectionEnabled":true,"popoutWholeStack":false,"blockedPopoutsThrowError":true,"closePopoutsOnUnload":true,"showPopoutIcon":true,"showMaximiseIcon":true,"showCloseIcon":true,"responsiveMode":"onload","tabOverlapAllowance":0,"reorderOnTabMenuClick":true,"tabControlOffset":10},"dimensions":{"borderWidth":5,"borderGrabWidth":15,"minItemHeight":10,"minItemWidth":10,"headerHeight":20,"dragProxyWidth":300,"dragProxyHeight":200},"labels":{"close":"close","maximise":"maximise","minimise":"minimise","popout":"open in new window","popin":"pop in","tabDropdown":"additional tabs"},"content":[{"type":"row","isClosable":true,"reorderEnabled":true,"title":"","content":[{"type":"stack","width":33.333333333333336,"isClosable":true,"reorderEnabled":true,"title":"","activeItemIndex":0,"content":[{"type":"component","componentName":"documentViewComponent","componentState":{"doc":"${doc3.Id}","title":"mini collection","scaling":1},"isClosable":true,"reorderEnabled":true,"title":"documentViewComponent"}]},{"type":"stack","width":33.333333333333336,"isClosable":true,"reorderEnabled":true,"title":"","activeItemIndex":0,"content":[{"type":"component","componentName":"documentViewComponent","componentState":{"doc":"${doc6.Id}","title":"","scaling":1},"isClosable":true,"reorderEnabled":true,"title":"documentViewComponent"}]},{"type":"stack","width":16.666666666666668,"isClosable":true,"reorderEnabled":true,"title":"","activeItemIndex":0,"content":[{"type":"component","componentName":"documentViewComponent","componentState":{"doc":"${doc4.Id}","title":"docking collection","scaling":1},"isClosable":true,"reorderEnabled":true,"title":"documentViewComponent"}]},{"type":"stack","isClosable":true,"reorderEnabled":true,"title":"","width":16.666666666666668,"activeItemIndex":0,"content":[{"type":"component","componentName":"documentViewComponent","componentState":{"doc":"${doc4.Id}","title":"hello"},"isClosable":true,"reorderEnabled":true,"title":"documentViewComponent"}]}]}],"isClosable":true,"reorderEnabled":true,"title":"","openPopouts":[],"maximisedItemId":null}` - mainContainer.SetText(KeyStore.Data, layoutstr) -} -//} -//); +let doc1 = Documents.TextDocument({ title: "hello", width: 400, height: 300 }); +let doc2 = doc1.MakeDelegate(); +doc2.Set(KS.X, new NumberField(150)); +doc2.Set(KS.Y, new NumberField(20)); +let doc3 = Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", { + x: 450, y: 100, title: "dog", width: 606, height: 386, nativeWidth: 606, nativeHeight: 386 +}); +//doc3.Set(KeyStore.Data, new ImageField); +const schemaDocs = Array.from(Array(5).keys()).map(v => Documents.ImageDocument("https://psmag.com/.image/t_share/MTMyNzc2NzM1MDY1MjgzMDM4/shutterstock_151341212jpg.jpg", { + x: 50 + 100 * v, y: 50, width: 100, height: 100, title: "cat" + v, nativeWidth: 606, nativeHeight: 386 +})); +schemaDocs.push(doc3); +schemaDocs[0].SetData(KS.Author, "Tyler", TextField); +schemaDocs[4].SetData(KS.Author, "Bob", TextField); +schemaDocs.push(doc2); +const doc7 = Documents.SchemaDocument(schemaDocs) +const docset = [doc1, doc2, doc3, doc7]; +let doc4 = Documents.CollectionDocument(docset, { + x: 0, y: 400, title: "mini collection" +}); +// let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { +// x: 650, y: 500, width: 600, height: 600, title: "cat 2" +// }); +let docset2 = [doc3, doc4, doc2]; +let doc6 = Documents.CollectionDocument(docset2, { + x: 350, y: 100, width: 600, height: 600, title: "docking collection" +}); + +var docs = [doc4, doc3, doc6].map(doc => CollectionDockingView.makeDocumentConfig(doc)); +var config = { + settings: { selectionEnabled: false }, content: [{ type: 'row', content: docs }] +}; +let mainContainer = Documents.DockDocument(JSON.stringify(config), { + x: 0, y: 0, title: "main container" +}) ReactDOM.render((
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 4c83186f8..9cc92712e 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,4 +1,3 @@ -import FlexLayout from "flexlayout-react"; import * as GoldenLayout from "golden-layout"; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; @@ -8,7 +7,6 @@ import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/Key"; import { ListField } from "../../../fields/ListField"; import { DragManager } from "../../util/DragManager"; -import { Transform } from "../../util/Transform"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; @@ -22,73 +20,37 @@ import { Server } from "../../Server"; @observer export class CollectionDockingView extends CollectionViewBase { - private static UseGoldenLayout = true; + public static Instance: CollectionDockingView; public static LayoutString() { return CollectionViewBase.LayoutString("CollectionDockingView"); } - private _containerRef = React.createRef(); - @computed - private get modelForGoldenLayout(): GoldenLayout { - var json = this.props.Document.GetText(KeyStore.Data, ""); - if (json != "") { - return new GoldenLayout(JSON.parse(json)) - } - const { fieldKey: fieldKey, Document: Document } = this.props; - const value: Document[] = Document.GetData(fieldKey, ListField, []); - var docs = value.map(doc => { - return { type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc.Id, title: doc.Title, scaling: 1 } }; - }); - return new GoldenLayout({ - settings: { - selectionEnabled: true - }, content: [{ type: 'row', content: docs }] - }); - } - constructor(props: CollectionViewProps) { - super(props); - } - - componentDidMount: () => void = () => { - if (this._containerRef.current && CollectionDockingView.UseGoldenLayout) { - this.goldenLayoutFactory(); - window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window + public static makeDocumentConfig(document: Document) { + return { + type: 'react-component', + component: 'DocumentFrameRenderer', + title: document.Title, + props: { + documentId: document.Id, + CollectionDockingView: CollectionDockingView.Instance, + } } } - componentWillUnmount: () => void = () => { - window.removeEventListener('resize', this.onResize); - } - private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })(); - - @action - onResize = (event: any) => { - var cur = this.props.ContainingDocumentView!.MainContent.current; - // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed - CollectionDockingView.myLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); - } + private _goldenLayout: any = null; + private _dragDiv: any = null; + private _dragParent: HTMLElement | null = null; + private _dragElement: HTMLDivElement | undefined; + private _dragFakeElement: HTMLDivElement | undefined; + private _containerRef = React.createRef(); + private _makeFullScreen: boolean = false; + private _maximizedStack: any = null; - @action - onPointerDown = (e: React.PointerEvent): void => { - if (e.button === 2 && this.active) { - e.stopPropagation(); - e.preventDefault(); - } else { - if (e.buttons === 1 && this.active) { - e.stopPropagation(); - } - } + constructor(props: CollectionViewProps) { + super(props); + CollectionDockingView.Instance = this; + (window as any).React = React; + (window as any).ReactDOM = ReactDOM; } - public static myLayout: any = null; - - private static _dragDiv: any = null; - private static _dragParent: HTMLElement | null = null; - private static _dragElement: HTMLDivElement; - private static _dragFakeElement: HTMLDivElement; - public static StartOtherDrag(dragElement: HTMLDivElement, dragDoc: Document) { - var newItemConfig = { - type: 'component', - componentName: 'documentViewComponent', - componentState: { doc: dragDoc.Id, title: dragDoc.Title, scaling: 1 } - }; + public StartOtherDrag(dragElement: HTMLDivElement, dragDoc: Document) { this._dragElement = dragElement; this._dragParent = dragElement.parentElement; // bcz: we want to copy this document into the header, not move it there. @@ -98,7 +60,7 @@ export class CollectionDockingView extends CollectionViewBase { this._dragDiv = document.createElement("div"); this._dragDiv.style.opacity = 0; DragManager.Root().appendChild(this._dragDiv); - CollectionDockingView.myLayout.createDragSource(this._dragDiv, newItemConfig); + this._goldenLayout.createDragSource(this._dragDiv, CollectionDockingView.makeDocumentConfig(dragDoc)); // - add our document to that div so that GoldenLayout will get the move events its listening for this._dragDiv.appendChild(this._dragElement); @@ -111,40 +73,29 @@ export class CollectionDockingView extends CollectionViewBase { // all of this must be undone when the document has been dropped (see tabCreated) } - _makeFullScreen: boolean = false; - _maximizedStack: any = null; - public static OpenFullScreen(document: Document) { - var newItemConfig = { - type: 'component', - componentName: 'documentViewComponent', - componentState: { doc: document.Id, title: document.Title } - }; - CollectionDockingView.myLayout._makeFullScreen = true; - CollectionDockingView.myLayout.root.contentItems[0].addChild(newItemConfig); + public OpenFullScreen(document: Document) { + this._makeFullScreen = true; + this._goldenLayout.root.contentItems[0].addChild(CollectionDockingView.makeDocumentConfig(document)); } - public static CloseFullScreen() { - if (CollectionDockingView.myLayout._maximizedStack != null) { - CollectionDockingView.myLayout._maximizedStack.header.controlsContainer.find('.lm_close').click(); - CollectionDockingView.myLayout._maximizedStack = null; + public CloseFullScreen() { + if (this._maximizedStack) { + this._maximizedStack.header.controlsContainer.find('.lm_close').click(); + this._maximizedStack = null; } } + // // Creates a vertical split on the right side of the docking view, and then adds the Document to that split // - public static AddRightSplit(document: Document) { - var newItemConfig = { - type: 'component', - componentName: 'documentViewComponent', - componentState: { doc: document.Id, title: document.Title } - } + public AddRightSplit(document: Document) { let newItemStackConfig = { type: 'stack', - content: [newItemConfig] + content: [CollectionDockingView.makeDocumentConfig(document)] }; - var newContentItem = new CollectionDockingView.myLayout._typeToItem[newItemStackConfig.type](CollectionDockingView.myLayout, newItemStackConfig, parent); + var newContentItem = new this._goldenLayout._typeToItem[newItemStackConfig.type](this._goldenLayout, newItemStackConfig, parent); - if (CollectionDockingView.myLayout.root.contentItems[0].isRow) { - var rowlayout = CollectionDockingView.myLayout.root.contentItems[0]; + if (this._goldenLayout.root.contentItems[0].isRow) { + var rowlayout = this._goldenLayout.root.contentItems[0]; var lastRowItem = rowlayout.contentItems[rowlayout.contentItems.length - 1]; lastRowItem.config["width"] *= 0.5; @@ -153,8 +104,8 @@ export class CollectionDockingView extends CollectionViewBase { rowlayout.callDownwards('setSize'); } else { - var collayout = CollectionDockingView.myLayout.root.contentItems[0]; - var newRow = collayout.layoutManager.createContentItem({ type: "row" }, CollectionDockingView.myLayout); + var collayout = this._goldenLayout.root.contentItems[0]; + var newRow = collayout.layoutManager.createContentItem({ type: "row" }, this._goldenLayout); collayout.parent.replaceChild(collayout, newRow); newRow.addChild(newContentItem, undefined, true); @@ -164,70 +115,75 @@ export class CollectionDockingView extends CollectionViewBase { newContentItem.config["width"] = 50; collayout.parent.callDownwards('setSize'); } - var state = JSON.stringify(CollectionDockingView.myLayout.toConfig()); - console.log(state); } - goldenLayoutFactory() { - CollectionDockingView.myLayout = this.modelForGoldenLayout; - var layout = CollectionDockingView.myLayout; - CollectionDockingView.myLayout.on('tabCreated', function (tab: any) { - if (CollectionDockingView._dragDiv) { - CollectionDockingView._dragDiv.removeChild(CollectionDockingView._dragElement); - CollectionDockingView._dragParent!.removeChild(CollectionDockingView._dragFakeElement); - CollectionDockingView._dragParent!.appendChild(CollectionDockingView._dragElement); - DragManager.Root().removeChild(CollectionDockingView._dragDiv); - CollectionDockingView._dragDiv = null; + componentDidMount: () => void = () => { + if (this._containerRef.current) { + + this._goldenLayout = new GoldenLayout(JSON.parse(this.props.Document.GetText(KeyStore.Data, ""))); + this._goldenLayout.on('tabCreated', this.tabCreated); + this._goldenLayout.on('stackCreated', this.stackCreated); + this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); + this._goldenLayout.container = this._containerRef.current; + this._goldenLayout.init(); + + window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window + } + } + componentWillUnmount: () => void = () => { + window.removeEventListener('resize', this.onResize); + } + @action + onResize = (event: any) => { + var cur = this.props.ContainingDocumentView!.MainContent.current; + + // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed + this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); + } + + @action + onPointerDown = (e: React.PointerEvent): void => { + if (e.button === 2 && this.active) { + e.stopPropagation(); + e.preventDefault(); + } else { + if (e.buttons === 1 && this.active) { + e.stopPropagation(); } - tab.setTitle(tab.contentItem.config.componentState.title); - tab.closeElement.off('click') //unbind the current click handler - .click(function () { - tab.contentItem.remove(); - }); - }); + } + } - CollectionDockingView.myLayout.on('stackCreated', function (stack: any) { - if (CollectionDockingView.myLayout._makeFullScreen) { - CollectionDockingView.myLayout._maximizedStack = stack; - CollectionDockingView.myLayout._maxstack = stack.header.controlsContainer.find('.lm_maximise'); + tabCreated = (tab: any) => { + { + if (this._dragDiv) { + this._dragDiv.removeChild(this._dragElement); + this._dragParent!.removeChild(this._dragFakeElement!); + this._dragParent!.appendChild(this._dragElement!); + DragManager.Root().removeChild(this._dragDiv); + this._dragDiv = null; } - //stack.header.controlsContainer.find('.lm_popout').hide(); - stack.header.controlsContainer.find('.lm_close') //get the close icon - .off('click') //unbind the current click handler + //tab.setTitle(tab.contentItem.config.componentState.title); + tab.closeElement.off('click') //unbind the current click handler .click(function () { - //if (confirm('really close this?')) { - stack.remove(); - //} + tab.contentItem.remove(); }); - }); + } + } - var me = this; - CollectionDockingView.myLayout.registerComponent('documentViewComponent', function (container: any, state: any) { - // bcz: this is crufty - // calling html() causes a div tag to be added in the DOM with id 'containingDiv'. - // Apparently, we need to wait to allow a live html div element to actually be instantiated. - // After a timeout, we lookup the live html div element and add our React DocumentView to it. - var containingDiv = "component_" + me.nextId(); - container.getElement().html("
"); - setTimeout(function () { - let divContainer = document.getElementById(containingDiv) as HTMLDivElement; - if (divContainer) { - let props: DockingProps = { - ContainingDiv: containingDiv, - DocumentId: state.doc, - Container: container, - CollectionDockingView: me, - HtmlElement: divContainer, - } - ReactDOM.render((), divContainer); - if (CollectionDockingView.myLayout._maxstack) { - CollectionDockingView.myLayout._maxstack.click(); - } - } - }, 0); - }); - CollectionDockingView.myLayout.container = this._containerRef.current; - CollectionDockingView.myLayout.init(); + stackCreated = (stack: any) => { + if (this._makeFullScreen) { + this._maximizedStack = stack; + setTimeout(function () { stack.header.controlsContainer.find('.lm_maximise').click() }, 10); + this._makeFullScreen = false; + } + //stack.header.controlsContainer.find('.lm_popout').hide(); + stack.header.controlsContainer.find('.lm_close') //get the close icon + .off('click') //unbind the current click handler + .click(function () { + //if (confirm('really close this?')) { + stack.remove(); + //} + }); } @@ -243,8 +199,8 @@ export class CollectionDockingView extends CollectionViewBase { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 5ec288b13..9405c820f 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -13,12 +13,10 @@ import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; +import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView"; @observer -export class CollectionSchemaView extends CollectionViewBase { - public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionSchemaView", fieldKey); } - +export class CollectionSchemaView extends React.Component { private _mainCont = React.createRef(); private DIVIDER_WIDTH = 5; @@ -34,6 +32,7 @@ export class CollectionSchemaView extends CollectionViewBase { doc: rowProps.value[0], fieldKey: rowProps.value[1], isSelected: () => false, + select: () => { }, isTopMost: false } let contents = ( @@ -112,7 +111,7 @@ export class CollectionSchemaView extends CollectionViewBase { // e.preventDefault(); // } else { - if (e.buttons === 1 && this.active) { + if (e.buttons === 1 && this.props.active()) { e.stopPropagation(); } } @@ -145,12 +144,12 @@ export class CollectionSchemaView extends CollectionViewBase { {({ measureRef }) =>
+ ContainingCollectionView={this.props.CollectionView} />
} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx new file mode 100644 index 000000000..651d85879 --- /dev/null +++ b/src/client/views/collections/CollectionView.tsx @@ -0,0 +1,116 @@ +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { Document } from "../../../fields/Document"; +import { Key } from "../../../fields/Key"; +import { ListField } from "../../../fields/ListField"; +import { SelectionManager } from "../../util/SelectionManager"; +import { ContextMenu } from "../ContextMenu"; +import React = require("react"); +import { Transform } from "../../util/Transform"; +import { KeyStore } from "../../../fields/KeyStore"; +import { NumberField } from "../../../fields/NumberField"; +import { CollectionFreeFormView } from "./CollectionFreeFormView"; +import { CollectionDockingView } from "./CollectionDockingView"; +import { CollectionSchemaView } from "./CollectionSchemaView"; +import { Opt } from "../../../fields/Field"; + + +export interface CollectionViewProps { + fieldKey: Key; + Document: Document; + ScreenToLocalTransform: () => Transform; + isSelected: () => boolean; + isTopMost: boolean; + select: (ctrlPressed: boolean) => void; + BackgroundView?: () => JSX.Element; +} + +export interface SubCollectionViewProps extends CollectionViewProps { + active: () => boolean; + addDocument: (doc: Document) => void; + removeDocument: (doc: Document) => boolean; + CollectionView: Opt; +} + +export enum CollectionViewType { + Invalid, + Freeform, + Schema, + Docking, +} + +export const COLLECTION_BORDER_WIDTH = 2; + +@observer +export class CollectionView extends React.Component { + + public static LayoutString(fieldKey: string = "DataKey") { + return ``; + } + public active = () => { + var isSelected = this.props.isSelected(); + var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); + var topMost = this.props.isTopMost; + return isSelected || childSelected || topMost; + } + @action + addDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array()) + value.push(doc); + } + + @action + removeDocument = (doc: Document): boolean => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array()) + let index = value.indexOf(doc); + if (index !== -1) { + value.splice(index, 1) + + SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems() + return true; + } + return false + } + + get collectionViewType(): CollectionViewType { + let Document = this.props.Document; + let viewField = Document.GetT(KeyStore.ViewType, NumberField); + if (viewField === "") { + return CollectionViewType.Invalid; + } else if (viewField) { + return viewField.Data; + } else { + return CollectionViewType.Freeform; + } + } + + set collectionViewType(type: CollectionViewType) { + let Document = this.props.Document; + Document.SetData(KeyStore.ViewType, type, NumberField); + } + + render() { + let viewType = this.collectionViewType; + switch (viewType) { + case CollectionViewType.Freeform: + return () + case CollectionViewType.Schema: + return () + case CollectionViewType.Docking: + return () + default: + return
+ } + } +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx deleted file mode 100644 index 0acc890d8..000000000 --- a/src/client/views/collections/CollectionViewBase.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { action, computed } from "mobx"; -import { observer } from "mobx-react"; -import { Document } from "../../../fields/Document"; -import { Key } from "../../../fields/Key"; -import { ListField } from "../../../fields/ListField"; -import { SelectionManager } from "../../util/SelectionManager"; -import { ContextMenu } from "../ContextMenu"; -import React = require("react"); -import { Transform } from "../../util/Transform"; - - -export interface CollectionViewProps { - fieldKey: Key; - Document: Document; - ScreenToLocalTransform: () => Transform; - isSelected: () => boolean; - isTopMost: boolean; - select: (ctrlPressed: boolean) => void; - BackgroundView?: () => JSX.Element; -} - -export const COLLECTION_BORDER_WIDTH = 2; - -@observer -export class CollectionViewBase extends React.Component { - - public static LayoutString(collectionType: string, fieldKey: string = "DataKey") { - return `<${collectionType} Document={Document} - ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select} - isTopMost={isTopMost} BackgroundView={BackgroundView} />`; - } - @computed - public get active(): boolean { - var isSelected = this.props.isSelected(); - var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.isTopMost; - return isSelected || childSelected || topMost; - } - @action - addDocument = (doc: Document): void => { - //TODO This won't create the field if it doesn't already exist - const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array()) - value.push(doc); - } - - @action - removeDocument = (doc: Document): boolean => { - //TODO This won't create the field if it doesn't already exist - const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array()) - let index = value.indexOf(doc); - if (index !== -1) { - value.splice(index, 1) - - SelectionManager.DeselectAll() - ContextMenu.Instance.clearItems() - return true; - } - return false - } - -} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7cf00a116..2114a5697 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -9,7 +9,7 @@ import { Utils } from "../../../Utils"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; -import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "../collections/CollectionViewBase"; +import { COLLECTION_BORDER_WIDTH, CollectionView, CollectionViewType } from "../collections/CollectionView"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import "./DocumentView.scss"; @@ -22,7 +22,7 @@ import { TextField } from "../../../fields/TextField"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? export interface DocumentViewProps { - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; Document: Document; AddDocument?: (doc: Document) => void; @@ -114,7 +114,7 @@ export class DocumentView extends React.Component { @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || - this.props.ContainingCollectionView.active; + this.props.ContainingCollectionView.active(); } private _contextMenuCanOpen = false; @@ -233,6 +233,9 @@ export class DocumentView extends React.Component { 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.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); } @@ -275,7 +278,7 @@ export class DocumentView extends React.Component { let backgroundLayout = this.backgroundLayout; if (backgroundLayout) { let backgroundView = () => ( { onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} > boolean; + select: () => void; isTopMost: boolean; } @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} isTopMost={isTopMost} />`; } + public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; } @computed get field(): FieldValue { const { doc, fieldKey } = this.props; diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts index 91977b243..a92c4a236 100644 --- a/src/fields/BasicField.ts +++ b/src/fields/BasicField.ts @@ -32,7 +32,7 @@ export abstract class BasicField extends Field { return; } let oldValue = this.data; - this.data = value; + this.setData(value); UndoManager.AddEvent({ undo: () => this.Data = oldValue, redo: () => this.Data = value @@ -40,6 +40,10 @@ export abstract class BasicField extends Field { Server.UpdateField(this); } + protected setData(value: T) { + this.data = value; + } + @action TrySetValue(value: any): boolean { if (typeof value == typeof this.data) { diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 6667485b6..ff13732b3 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -90,6 +90,15 @@ export class Document extends Field { return field; } + GetAsync(key: Key, callback: (field: Field) => void): boolean { + //This currently doesn't deal with prototypes + if (this._proxies.has(key.Id)) { + Server.GetDocumentField(this, key, callback); + return true; + } + return false; + } + GetT(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): FieldValue { var getfield = this.Get(key, ignoreProto); if (getfield != FieldWaiting) { diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 7056886aa..6d6c6a546 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -16,6 +16,7 @@ export namespace KeyStore { export const ZIndex = new Key("ZIndex"); export const Data = new Key("Data"); export const Annotations = new Key("Annotations"); + export const ViewType = new Key("ViewType"); export const Layout = new Key("Layout"); export const BackgroundLayout = new Key("BackgroundLayout"); export const LayoutKeys = new Key("LayoutKeys"); diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index 75c2eb343..700600804 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -1,4 +1,4 @@ -import { action, IArrayChange, IArraySplice, IObservableArray, observe } from "mobx"; +import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx"; import { Server } from "../client/Server"; import { UndoManager } from "../client/util/UndoManager"; import { Types } from "../server/Message"; @@ -13,7 +13,12 @@ export class ListField extends BasicField { if (save) { Server.UpdateField(this); } - observe(this.Data as IObservableArray, (change: IArrayChange | IArraySplice) => { + this.observeList(); + } + + private observeDisposer: Lambda | undefined; + private observeList(): void { + this.observeDisposer = observe(this.Data as IObservableArray, (change: IArrayChange | IArraySplice) => { this.updateProxies() if (change.type == "splice") { UndoManager.AddEvent({ @@ -27,7 +32,15 @@ export class ListField extends BasicField { }) } Server.UpdateField(this); - }) + }); + } + + protected setData(value: T[]) { + if (this.observeDisposer) { + this.observeDisposer() + } + this.data = observable(value); + this.observeList(); } private updateProxies() { -- cgit v1.2.3-70-g09d2 From 370ac4fde98f2ff25b2dedd6b0857c88c6d7f55b Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 22 Feb 2019 13:42:25 -0500 Subject: moved common code into CollectionViewBase. --- src/client/documents/Documents.ts | 2 +- src/client/util/DragManager.ts | 1 + .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionFreeFormView.tsx | 59 ++-------------- .../views/collections/CollectionSchemaView.tsx | 13 ++-- src/client/views/collections/CollectionView.tsx | 20 +----- .../views/collections/CollectionViewBase.tsx | 82 ++++++++++++++++++++++ 7 files changed, 102 insertions(+), 78 deletions(-) create mode 100644 src/client/views/collections/CollectionViewBase.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bfa6cb7a9..156a09316 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,7 +13,7 @@ import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormV import { FieldId } from "../../fields/Field"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; -interface DocumentOptions { +export interface DocumentOptions { x?: number; y?: number; width?: number; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6d5fe12a7..eb4b3aeaa 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -44,6 +44,7 @@ export namespace DragManager { drop: (e: Event, de: DropEvent) => void; } + export function MakeDropTarget(element: HTMLElement, options: DropOptions): DragDropDisposer { if ("canDrop" in element.dataset) { throw new Error("Element is already droppable, can't make it droppable again"); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 752007439..857af023d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -14,8 +14,9 @@ import { DragManager } from "../../util/DragManager"; import { undoBatch } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; -import { CollectionViewProps, COLLECTION_BORDER_WIDTH, SubCollectionViewProps } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; import React = require("react"); +import { SubCollectionViewProps } from "./CollectionViewBase"; @observer export class CollectionDockingView extends React.Component { diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index b78b1a3b6..b8312aff7 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -4,20 +4,18 @@ import { action, computed, trace } from "mobx"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DragManager } from "../../util/DragManager"; import "./CollectionFreeFormView.scss"; -import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { KeyStore } from "../../../fields/KeyStore"; import { Document } from "../../../fields/Document"; import { ListField } from "../../../fields/ListField"; -import { NumberField } from "../../../fields/NumberField"; -import { Documents } from "../../documents/Documents"; import { FieldWaiting } from "../../../fields/Field"; import { Transform } from "../../util/Transform"; import { DocumentView } from "../nodes/DocumentView"; import { undoBatch } from "../../util/UndoManager"; -import { jSXElement } from "babel-types"; +import { CollectionViewBase, SubCollectionViewProps } from "./CollectionViewBase"; @observer -export class CollectionFreeFormView extends React.Component { +export class CollectionFreeFormView extends CollectionViewBase { private _canvasRef = React.createRef(); private _lastX: number = 0; private _lastY: number = 0; @@ -45,13 +43,8 @@ export class CollectionFreeFormView extends React.Component { + super.drop(e, de); const doc: DocumentView = de.data["document"]; - if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this.props.CollectionView) { - if (doc.props.RemoveDocument) { - doc.props.RemoveDocument(doc.props.Document); - } - this.props.addDocument(doc.props.Document); - } const xOffset = de.data["xOffset"] as number || 0; const yOffset = de.data["yOffset"] as number || 0; //this should be able to use translate and scale methods on an Identity transform, no? @@ -62,21 +55,6 @@ export class CollectionFreeFormView extends React.Component { - if (this.dropDisposer) { - this.dropDisposer(); - } - if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, { - handlers: { - drop: this.drop - } - }); - } } @action @@ -148,36 +126,11 @@ export class CollectionFreeFormView extends React.Component { - e.stopPropagation() - e.preventDefault() - let fReader = new FileReader() - let file = e.dataTransfer.items[0].getAsFile(); - let that = this; const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0); const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0); let x = e.pageX - panx let y = e.pageY - pany - - fReader.addEventListener("load", action("drop", () => { - if (fReader.result) { - let url = "" + fReader.result; - let doc = Documents.ImageDocument(url, { - x: x, y: y - }) - let docs = that.props.Document.GetT(KeyStore.Data, ListField); - if (docs != FieldWaiting) { - if (!docs) { - docs = new ListField(); - that.props.Document.Set(KeyStore.Data, docs) - } - docs.Data.push(doc); - } - } - }), false) - - if (file) { - fReader.readAsDataURL(file) - } + super.onDrop(e, { x: x, y: y }); } onDragOver = (): void => { @@ -249,7 +202,7 @@ export class CollectionFreeFormView extends React.Component e.preventDefault()} - onDrop={this.onDrop} + onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} ref={this.createDropTarget}> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f25e721c0..df732308a 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -5,7 +5,7 @@ import Measure from "react-measure"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; import "react-table/react-table.css"; import { Document } from "../../../fields/Document"; -import { Field } from "../../../fields/Field"; +import { Field, FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; @@ -13,10 +13,11 @@ import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { CollectionViewBase } from "./CollectionViewBase"; @observer -export class CollectionSchemaView extends React.Component { +export class CollectionSchemaView extends CollectionViewBase { private _mainCont = React.createRef(); private DIVIDER_WIDTH = 5; @@ -117,6 +118,7 @@ export class CollectionSchemaView extends React.Component { return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._parentScaling); } @@ -181,7 +183,10 @@ export class CollectionSchemaView extends React.Component
-
+
this.onDrop(e, {})} + ref={this.createDropTarget} + style={{ position: "relative", float: "left", width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)`, height: "100%" }}> {content}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 651d85879..ff1803ec3 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,36 +1,18 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; -import { Key } from "../../../fields/Key"; import { ListField } from "../../../fields/ListField"; import { SelectionManager } from "../../util/SelectionManager"; import { ContextMenu } from "../ContextMenu"; import React = require("react"); -import { Transform } from "../../util/Transform"; import { KeyStore } from "../../../fields/KeyStore"; import { NumberField } from "../../../fields/NumberField"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionSchemaView } from "./CollectionSchemaView"; -import { Opt } from "../../../fields/Field"; +import { CollectionViewProps } from "./CollectionViewBase"; -export interface CollectionViewProps { - fieldKey: Key; - Document: Document; - ScreenToLocalTransform: () => Transform; - isSelected: () => boolean; - isTopMost: boolean; - select: (ctrlPressed: boolean) => void; - BackgroundView?: () => JSX.Element; -} - -export interface SubCollectionViewProps extends CollectionViewProps { - active: () => boolean; - addDocument: (doc: Document) => void; - removeDocument: (doc: Document) => boolean; - CollectionView: Opt; -} export enum CollectionViewType { Invalid, diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx new file mode 100644 index 000000000..06de56383 --- /dev/null +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -0,0 +1,82 @@ +import { action, computed } from "mobx"; +import { Document } from "../../../fields/Document"; +import { ListField } from "../../../fields/ListField"; +import React = require("react"); +import { KeyStore } from "../../../fields/KeyStore"; +import { Opt, FieldWaiting } from "../../../fields/Field"; +import { undoBatch } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; +import { DocumentView } from "../nodes/DocumentView"; +import { Documents, DocumentOptions } from "../../documents/Documents"; +import { Key } from "../../../fields/Key"; +import { Transform } from "../../util/Transform"; + + +export interface CollectionViewProps { + fieldKey: Key; + Document: Document; + ScreenToLocalTransform: () => Transform; + isSelected: () => boolean; + isTopMost: boolean; + select: (ctrlPressed: boolean) => void; + BackgroundView?: () => JSX.Element; +} +export interface SubCollectionViewProps extends CollectionViewProps { + active: () => boolean; + addDocument: (doc: Document) => void; + removeDocument: (doc: Document) => boolean; + CollectionView: any; +} + +export class CollectionViewBase extends React.Component { + private dropDisposer?: DragManager.DragDropDisposer; + protected createDropTarget = (ele: HTMLDivElement) => { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + } + } + + @undoBatch + @action + protected drop(e: Event, de: DragManager.DropEvent) { + const doc: DocumentView = de.data["document"]; + if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this.props.CollectionView) { + if (doc.props.RemoveDocument) { + doc.props.RemoveDocument(doc.props.Document); + } + this.props.addDocument(doc.props.Document); + } + e.stopPropagation(); + } + + @action + protected onDrop(e: React.DragEvent, options: DocumentOptions): void { + e.stopPropagation() + e.preventDefault() + let fReader = new FileReader() + let file = e.dataTransfer.items[0].getAsFile(); + let that = this; + + fReader.addEventListener("load", action("drop", () => { + if (fReader.result) { + let url = "" + fReader.result; + let doc = Documents.ImageDocument(url, options) + let docs = that.props.Document.GetT(KeyStore.Data, ListField); + if (docs != FieldWaiting) { + if (!docs) { + docs = new ListField(); + that.props.Document.Set(KeyStore.Data, docs) + } + docs.Data.push(doc); + } + } + }), false) + + if (file) { + fReader.readAsDataURL(file) + } + } +} -- cgit v1.2.3-70-g09d2 From f4ec29ad3143cf13bf7fa05985926242cabb6b1a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 23 Feb 2019 02:34:58 -0500 Subject: css hacking to fix layout issues related to making a captioned image style --- src/client/documents/Documents.ts | 20 ++++++++++++++++---- .../views/collections/CollectionFreeFormView.scss | 2 ++ .../views/collections/CollectionFreeFormView.tsx | 5 +++-- src/client/views/nodes/FieldView.tsx | 3 ++- src/client/views/nodes/FormattedTextBox.scss | 4 ++-- src/client/views/nodes/FormattedTextBox.tsx | 5 ++--- src/client/views/nodes/ImageBox.scss | 3 ++- src/fields/KeyStore.ts | 1 + 8 files changed, 30 insertions(+), 13 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 156a09316..6ec5aa711 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -5,13 +5,10 @@ import { TextField } from "../../fields/TextField"; import { NumberField } from "../../fields/NumberField"; import { ListField } from "../../fields/ListField"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; -import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { CollectionSchemaView } from "../views/collections/CollectionSchemaView"; import { ImageField } from "../../fields/ImageField"; import { ImageBox } from "../views/nodes/ImageBox"; -import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormView"; -import { FieldId } from "../../fields/Field"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { FieldView } from "../views/nodes/FieldView"; export interface DocumentOptions { x?: number; @@ -102,12 +99,27 @@ export namespace Documents { return imageProto; } return imageProto; + } + // example of custom display string for an image that shows a caption. + function CaptionLayoutString() { + return `
+
` + + ImageBox.LayoutString() + + `
+
` + + FormattedTextBox.LayoutString("CaptionKey") + + `
+
` }; + export function ImageDocument(url: string, options: DocumentOptions = {}): Document { let doc = GetImagePrototype().MakeDelegate(); setupOptions(doc, options); doc.Set(KeyStore.Data, new ImageField(new URL(url))); + doc.Set(KeyStore.Caption, new TextField("my caption...")); + doc.Set(KeyStore.BackgroundLayout, new TextField(CaptionLayoutString())); + doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption])); let annotation = Documents.TextDocument({ title: "hello" }); doc.Set(KeyStore.Annotations, new ListField([annotation])); diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss index 4cf474f77..6d678528a 100644 --- a/src/client/views/collections/CollectionFreeFormView.scss +++ b/src/client/views/collections/CollectionFreeFormView.scss @@ -11,5 +11,7 @@ position: absolute; top: 0; left: 0; + width:100%; + height: 100% } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index c12a82617..6d2662105 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -34,7 +34,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) } @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) } @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); } - @computed get isAnnotationOverlay() { return this.props.fieldKey == KeyStore.Annotations; } + @computed get isAnnotationOverlay() { return this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); } @@ -91,6 +91,7 @@ export class CollectionFreeFormView extends CollectionViewBase { let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); this.SetPan(x + dx, y + dy); + console.log("px = " + x + " " + y) } this._lastX = e.pageX; this._lastY = e.pageY; @@ -209,7 +210,7 @@ export class CollectionFreeFormView extends CollectionViewBase { style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} ref={this.createDropTarget}>
{this.backgroundView} {this.views} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 97d3f2a85..9dda08d8d 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -27,7 +27,8 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; } + 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 { const { doc, fieldKey } = this.props; diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 5139d5d6b..872a2138b 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 { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 16728d471..c0969a8c3 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -10,8 +10,6 @@ import "./FormattedTextBox.scss"; import React = require("react") import { RichTextField } from "../../../fields/RichTextField"; import { FieldViewProps, FieldView } from "./FieldView"; -import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; -import { observer } from "mobx-react"; // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document @@ -32,7 +30,7 @@ import { observer } from "mobx-react"; //] export class FormattedTextBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(FormattedTextBox) } + public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) } private _ref: React.RefObject; private _editorView: Opt; private _reactionDisposer: Opt; @@ -118,6 +116,7 @@ export class FormattedTextBox extends React.Component { 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/fields/KeyStore.ts b/src/fields/KeyStore.ts index 6d6c6a546..42e3f6b58 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -22,5 +22,6 @@ export namespace KeyStore { export const LayoutKeys = new Key("LayoutKeys"); export const LayoutFields = new Key("LayoutFields"); export const ColumnsKey = new Key("SchemaColumns"); + export const Caption = new Key("Caption"); export const ActiveFrame = new Key("ActiveFrame"); } -- cgit v1.2.3-70-g09d2 From 6c451fa68db11c80f452ca7c85242ad98d867ab0 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 23 Feb 2019 04:49:48 -0500 Subject: Added web clippings on drag and drop --- src/client/documents/Documents.ts | 24 +++++++++++++++++++++ .../views/collections/CollectionFreeFormView.tsx | 3 ++- .../views/collections/CollectionViewBase.tsx | 9 ++++++++ src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/FieldView.tsx | 4 ++++ src/client/views/nodes/WebView.tsx | 22 +++++++++++++++++++ src/fields/HtmlField.ts | 25 ++++++++++++++++++++++ src/fields/KeyStore.ts | 1 + src/fields/TextField.ts | 2 +- src/server/Message.ts | 2 +- src/server/ServerUtil.ts | 6 +++++- 11 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 src/client/views/nodes/WebView.tsx create mode 100644 src/fields/HtmlField.ts (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6ec5aa711..d8e1db0b0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -9,6 +9,8 @@ import { ImageField } from "../../fields/ImageField"; import { ImageBox } from "../views/nodes/ImageBox"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { FieldView } from "../views/nodes/FieldView"; +import { HtmlField } from "../../fields/HtmlField"; +import { WebView } from "../views/nodes/WebView"; export interface DocumentOptions { x?: number; @@ -79,6 +81,28 @@ export namespace Documents { return doc; } + let htmlProto: Document; + const htmlProtoId = "htmlProto"; + function GetHtmlPrototype(): Document { + if (!htmlProto) { + htmlProto = new Document(htmlProtoId); + htmlProto.Set(KeyStore.X, new NumberField(0)); + htmlProto.Set(KeyStore.Y, new NumberField(0)); + htmlProto.Set(KeyStore.Width, new NumberField(300)); + htmlProto.Set(KeyStore.Height, new NumberField(150)); + htmlProto.Set(KeyStore.Layout, new TextField(WebView.LayoutString())); + htmlProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data])); + } + return htmlProto; + } + + export function HtmlDocument(html: string, options: DocumentOptions = {}): Document { + let doc = GetHtmlPrototype().MakeDelegate(); + setupOptions(doc, options); + doc.Set(KeyStore.Data, new HtmlField(html)); + return doc; + } + let imageProto: Document; const imageProtoId = "imageProto"; function GetImagePrototype(): Document { diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 4799eda97..b031c35a7 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -13,6 +13,7 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionView } from "../collections/CollectionView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; +import { WebView } from "../nodes/WebView"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; import "./CollectionFreeFormView.scss"; @@ -185,7 +186,7 @@ export class CollectionFreeFormView extends CollectionViewBase { get backgroundView() { return !this.backgroundLayout ? (null) : ( e.preventDefault() let that = this; + let html = e.dataTransfer.getData("text/html"); + let text = e.dataTransfer.getData("text/plain"); + if (html) { + let htmlDoc = Documents.HtmlDocument(html, { ...options }); + htmlDoc.SetText(KeyStore.DocumentText, text); + this.props.addDocument(htmlDoc); + return; + } + for (let i = 0; i < e.dataTransfer.items.length; i++) { let item = e.dataTransfer.items[i]; if (item.kind === "string" && item.type.indexOf("uri") != -1) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a9e211431..ad1328e5d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -12,6 +12,7 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; +import { WebView } from "./WebView"; import { ContextMenu } from "../ContextMenu"; import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { ImageBox } from "../nodes/ImageBox"; @@ -196,7 +197,7 @@ export class DocumentView extends React.Component { @computed get mainContent() { return { } else if (field instanceof NumberField) { return

{field.Data}

+ } else if (field instanceof HtmlField) { + return } else if (field != FieldWaiting) { return

{field.GetValue}

} else 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 { + 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 + } +} \ No newline at end of file diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts new file mode 100644 index 000000000..a07326095 --- /dev/null +++ b/src/fields/HtmlField.ts @@ -0,0 +1,25 @@ +import { BasicField } from "./BasicField"; +import { Types } from "../server/Message"; +import { FieldId } from "./Field"; + +export class HtmlField extends BasicField { + constructor(data: string = "", id?: FieldId, save: boolean = true) { + super(data, save, id); + } + + ToScriptString(): string { + return `new HtmlField("${this.Data}")`; + } + + Copy() { + return new HtmlField(this.Data); + } + + ToJson(): { _id: string; type: Types; data: any; } { + return { + type: Types.Html, + data: this.Data, + _id: this.Id, + } + } +} \ No newline at end of file diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 42e3f6b58..290fa2be7 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -24,4 +24,5 @@ export namespace KeyStore { export const ColumnsKey = new Key("SchemaColumns"); export const Caption = new Key("Caption"); export const ActiveFrame = new Key("ActiveFrame"); + export const DocumentText = new Key("DocumentText"); } diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts index ad96ab6d9..71d8ea310 100644 --- a/src/fields/TextField.ts +++ b/src/fields/TextField.ts @@ -22,4 +22,4 @@ export class TextField extends BasicField { _id: this.Id } } -} +} \ No newline at end of file diff --git a/src/server/Message.ts b/src/server/Message.ts index 7f3190f7f..80fc9a80d 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -45,7 +45,7 @@ export class GetFieldArgs { } export enum Types { - Number, List, Key, Image, Document, Text, RichText, DocumentReference + Number, List, Key, Image, Document, Text, RichText, DocumentReference, Html } export class DocumentTransfer implements Transferable { diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index 46c409ec4..08e72fdae 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -9,6 +9,7 @@ import { Document } from './../fields/Document'; import { Server } from './../client/Server'; import { Types } from './Message'; import { Utils } from '../Utils'; +import { HtmlField } from '../fields/HtmlField'; export class ServerUtils { public static FromJson(json: any): Field { @@ -27,6 +28,8 @@ export class ServerUtils { return new NumberField(data, id, false) case Types.Text: return new TextField(data, id, false) + case Types.Html: + return new HtmlField(data, id, false) case Types.RichText: return new RichTextField(data, id, false) case Types.Key: @@ -42,7 +45,8 @@ export class ServerUtils { doc._proxies.set(element[0], element[1]); }); return doc + default: + throw Error("Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here"); } - return new TextField(data, id) } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 96327e5b393ba967a05b6c985a2641e3705c9e2c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 23 Feb 2019 10:12:39 -0500 Subject: added an overlay layer to collections... but it blocks events. not sure how to fix. --- src/client/documents/Documents.ts | 7 +++++++ .../views/collections/CollectionFreeFormView.tsx | 24 ++++++++++++++++++++++ src/fields/KeyStore.ts | 1 + 3 files changed, 32 insertions(+) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6ec5aa711..e0d44e535 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -112,6 +112,12 @@ export namespace Documents { + FormattedTextBox.LayoutString("CaptionKey") + `
` }; + function OverlayLayoutString() { + return `
+
` + + FormattedTextBox.LayoutString("CaptionKey") + + `
+
` }; export function ImageDocument(url: string, options: DocumentOptions = {}): Document { let doc = GetImagePrototype().MakeDelegate(); @@ -119,6 +125,7 @@ export namespace Documents { doc.Set(KeyStore.Data, new ImageField(new URL(url))); doc.Set(KeyStore.Caption, new TextField("my caption...")); doc.Set(KeyStore.BackgroundLayout, new TextField(CaptionLayoutString())); + //doc.Set(KeyStore.OverlayLayout, new TextField(OverlayLayoutString())); doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption])); let annotation = Documents.TextDocument({ title: "hello" }); diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 4799eda97..ee0f3730d 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -161,6 +161,12 @@ export class CollectionFreeFormView extends CollectionViewBase { return field.Data; } } + @computed get overlayLayout(): string | undefined { + let field = this.props.Document.GetT(KeyStore.OverlayLayout, TextField); + if (field && field !== "") { + return field.Data; + } + } @computed get views() { const { fieldKey, Document } = this.props; @@ -192,6 +198,17 @@ export class CollectionFreeFormView extends CollectionViewBase { onError={(test: any) => console.log(test)} />); } + @computed + get overlayView() { + return !this.overlayLayout ? (null) : + ( console.log(test)} + />); + } getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform()) getLocalTransform = (): Transform => Transform.Identity.translate(-this.panX, -this.panY).scale(1 / this.scale); noScaling = () => 1; @@ -199,6 +216,12 @@ export class CollectionFreeFormView extends CollectionViewBase { render() { const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0); const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0); + var overlay = this.overlayView ? +
+ {this.overlayView} +
+ : + (null); return (
+ {overlay}
); } diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 42e3f6b58..78ed09efd 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -19,6 +19,7 @@ export namespace KeyStore { export const ViewType = new Key("ViewType"); export const Layout = new Key("Layout"); export const BackgroundLayout = new Key("BackgroundLayout"); + export const OverlayLayout = new Key("OverlayLayout"); export const LayoutKeys = new Key("LayoutKeys"); export const LayoutFields = new Key("LayoutFields"); export const ColumnsKey = new Key("SchemaColumns"); -- cgit v1.2.3-70-g09d2 From 41ff4813ddd9e6094d7d609c5960e1a614e00d7f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 23 Feb 2019 11:38:15 -0500 Subject: working overlays --- src/client/documents/Documents.ts | 14 +++++++------- src/client/views/collections/CollectionFreeFormView.scss | 9 +++++++++ src/client/views/collections/CollectionFreeFormView.tsx | 6 +++--- src/client/views/nodes/FormattedTextBox.scss | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 72efb45f5..4c5f26fbd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -127,18 +127,18 @@ export namespace Documents { } // example of custom display string for an image that shows a caption. - function CaptionLayoutString() { + function EmbeddedCaption() { return `
` + ImageBox.LayoutString() + `
-
` +
` + FormattedTextBox.LayoutString("CaptionKey") + `
` }; - function OverlayLayoutString() { - return `
-
` + function FixedCaption() { + return `
+
` + FormattedTextBox.LayoutString("CaptionKey") + `
` }; @@ -148,8 +148,8 @@ export namespace Documents { setupOptions(doc, options); doc.Set(KeyStore.Data, new ImageField(new URL(url))); doc.Set(KeyStore.Caption, new TextField("my caption...")); - doc.Set(KeyStore.BackgroundLayout, new TextField(CaptionLayoutString())); - //doc.Set(KeyStore.OverlayLayout, new TextField(OverlayLayoutString())); + doc.Set(KeyStore.BackgroundLayout, new TextField(EmbeddedCaption())); + doc.Set(KeyStore.OverlayLayout, new TextField(FixedCaption())); doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption])); let annotation = Documents.TextDocument({ title: "hello" }); diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss index 6d678528a..e682b9815 100644 --- a/src/client/views/collections/CollectionFreeFormView.scss +++ b/src/client/views/collections/CollectionFreeFormView.scss @@ -1,4 +1,13 @@ .collectionfreeformview-container { + + ::-webkit-scrollbar { + -webkit-appearance: none; + width: 10px; + } + ::-webkit-scrollbar-thumb { + border-radius: 5px; + background-color: rgba(0,0,0,.5); + } border-style: solid; box-sizing: border-box; position: relative; diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 85f7d5677..cb6668634 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -120,8 +120,8 @@ export class CollectionFreeFormView extends CollectionViewBase { private SetPan(panX: number, panY: number) { const newPanX = Math.max((1 - this.zoomScaling) * this.nativeWidth, Math.min(0, panX)); const newPanY = Math.max((1 - this.zoomScaling) * this.nativeHeight, Math.min(0, panY)); - this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX); - this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY); + this.props.Document.SetNumber(KeyStore.PanX, false && this.isAnnotationOverlay ? newPanX : panX); + this.props.Document.SetNumber(KeyStore.PanY, false && this.isAnnotationOverlay ? newPanY : panY); } @action @@ -238,7 +238,7 @@ export class CollectionFreeFormView extends CollectionViewBase { {this.backgroundView} {this.views}
- {overlay} + {this.overlayView}
); } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 872a2138b..226456fab 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -10,5 +10,5 @@ .formattedTextBox-cont { background: beige; - padding: 1vw; + padding: 0; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f4a01b9b394a5bdc48e57923567249bb05cb71ff Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 8 Mar 2019 00:11:04 -0500 Subject: Close to refactor --- .vscode/launch.json | 2 +- src/client/SocketStub.ts | 1 + src/client/documents/Documents.ts | 6 +- src/client/views/Main.tsx | 79 ++++++++++--------- .../authentication/controllers/WorkspacesMenu.tsx | 56 ++++---------- src/server/database.ts | 10 +++ src/server/index.ts | 88 +++++++++++----------- 7 files changed, 117 insertions(+), 125 deletions(-) (limited to 'src/client/documents') diff --git a/.vscode/launch.json b/.vscode/launch.json index 5f320d3fe..fb91a1080 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "name": "Launch Chrome against localhost", "sourceMaps": true, "breakOnLoad": true, - "url": "http://localhost:1050/signup", + "url": "http://localhost:1050/login", "webRoot": "${workspaceFolder}", }, { diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts index 18df4ca0a..c48f21f63 100644 --- a/src/client/SocketStub.ts +++ b/src/client/SocketStub.ts @@ -48,6 +48,7 @@ export class SocketStub { public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: { [key: string]: Field }) => any) { Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: any[]) => { + console.log(fieldIds); let fieldMap: any = {}; for (let field of fields) { fieldMap[field._id] = ServerUtils.FromJson(field); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1d24ff7d2..596652cf0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -44,14 +44,14 @@ export namespace Documents { const collProtoId = "collectionProto"; const kvpProtoId = "kvpProto"; - export function initProtos(mainDocId: string, callback: (mainDoc?: Document) => void) { - Server.GetFields([collProtoId, textProtoId, imageProtoId, mainDocId], (fields) => { + export function initProtos(callback: () => void) { + Server.GetFields([collProtoId, textProtoId, imageProtoId], (fields) => { collProto = fields[collProtoId] as Document; 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) + callback(); }); } function assignOptions(doc: Document, options: DocumentOptions): Document { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 2f4264ad9..da7e17f1f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -19,13 +19,14 @@ import { DocumentDecorations } from './DocumentDecorations'; import { DocumentView } from './nodes/DocumentView'; import "./Main.scss"; import { observer } from 'mobx-react'; +import { Field, Opt } from '../../fields/Field'; @observer export class Main extends React.Component { // dummy initializations keep the compiler happy - @observable private mainDocId = ""; - @observable private mainContainer: Document = new Document; - @observable private mainfreeform: Document = new Document; + @observable private mainContainer?: Document; + @observable private mainfreeform?: Document; + @observable private userWorkspaces: Document[] = []; constructor(props: Readonly<{}>) { super(props); @@ -33,7 +34,9 @@ export class Main extends React.Component { configure({ enforceActions: "observed" }); this.initEventListeners(); - this.initAuthenticationRouters(); + Documents.initProtos(() => { + this.initAuthenticationRouters(); + }); } initEventListeners = () => { @@ -50,46 +53,49 @@ export class Main extends React.Component { initAuthenticationRouters = () => { // Load the user's active workspace, or create a new one if initial session after signup request.get(this.contextualize("getActiveWorkspaceId"), (error, response, body) => { - this.requestWorkspace(body ? body : this.getNewWorkspace()); + if (body) { + Server.GetField(body, field => { + if (field instanceof Document) { + this.openDocument(field); + } else { + this.createNewWorkspace(); + } + }); + } else { + this.createNewWorkspace(); + } }); } - getNewWorkspace = (): string => { - let newId = Utils.GenerateGuid(); + @action + createNewWorkspace = (): void => { + let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }); + let newId = mainDoc.Id; request.post(this.contextualize("addWorkspaceId"), { body: { target: newId }, json: true - }) - return newId; + }); + + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + setTimeout(() => { + let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); + + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; + mainDoc.SetText(KeyStore.Data, JSON.stringify(dockingLayout)); + mainDoc.Set(KeyStore.ActiveFrame, freeformDoc); + this.openDocument(mainDoc); + }, 0); + this.userWorkspaces.push(mainDoc); } @action - requestWorkspace = (activeWorkspaceId: string) => { + openDocument = (doc: Document): void => { request.post(this.contextualize("setActiveWorkspaceId"), { - body: { target: activeWorkspaceId }, + body: { target: doc.Id }, json: true - }) - Documents.initProtos(activeWorkspaceId, this.prepareWorkspace); - } - - @action.bound - prepareWorkspace = (res?: Document) => { - if (res instanceof Document) { - this.mainContainer = res; - this.mainContainer.GetAsync(KeyStore.ActiveFrame, field => this.mainfreeform = field as Document); - } - else { - this.mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, this.mainDocId); - - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => { - this.mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); - - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(this.mainfreeform)] }] }; - this.mainContainer.SetText(KeyStore.Data, JSON.stringify(dockingLayout)); - this.mainContainer.Set(KeyStore.ActiveFrame, this.mainfreeform); - }, 0); - } + }); + this.mainContainer = doc; + this.mainContainer.GetAsync(KeyStore.ActiveFrame, field => this.mainfreeform = field as Document); } toggleWorkspaces = () => { @@ -117,8 +123,11 @@ export class Main extends React.Component { let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" })); let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addClick = (creator: () => Document) => action(() => this.mainfreeform.GetList(KeyStore.Data, []).push(creator())); + let addClick = (creator: () => Document) => action(() => this.mainfreeform!.GetList(KeyStore.Data, []).push(creator())); + if (!this.mainContainer) { + return
+ } return (
- +
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index b565af193..ebc4f5a10 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -4,62 +4,37 @@ import { observable, action, configure, reaction, computed, ObservableMap, runIn import { observer } from "mobx-react"; import * as request from 'request' import './WorkspacesMenu.css' +import { Document } from '../../../fields/Document'; +import { Server } from '../../../client/Server'; +import { Field } from '../../../fields/Field'; export interface WorkspaceMenuProps { - active: string; - load: (workspaceId: string) => void; - new: () => string; + active: Document; + open: (workspace: Document) => void; + new: () => void; + allWorkspaces: Document[]; } @observer export class WorkspacesMenu extends React.Component { static Instance: WorkspacesMenu; @observable private workspacesExposed: boolean = false; - @observable private workspaceIds: Array = []; - @observable private selectedWorkspaceId: string = ""; constructor(props: WorkspaceMenuProps) { super(props); WorkspacesMenu.Instance = this; - this.loadExistingWorkspace = this.loadExistingWorkspace.bind(this); this.addNewWorkspace = this.addNewWorkspace.bind(this); - this.selectedWorkspaceId = this.props.active; } @action addNewWorkspace() { - let newId = this.props.new(); - this.selectedWorkspaceId = newId; - this.props.load(newId); + this.props.new(); this.toggle(); } - @action - loadExistingWorkspace = (e: React.MouseEvent) => { - let id = e.currentTarget.innerHTML; - this.props.load(id); - this.selectedWorkspaceId = id; - } - @action toggle() { - if (this.workspacesExposed) { - this.workspacesExposed = !this.workspacesExposed; - } else { - request.get(window.location.origin + "/getAllWorkspaceIds", this.idCallback) - } - } - - @action.bound - idCallback: request.RequestCallback = (error, response, body) => { - this.workspaceIds = []; - let ids: Array = JSON.parse(body) as Array; - if (ids) { - for (let i = 0; i < ids.length; i++) { - this.workspaceIds.push(ids[i]); - } - this.workspacesExposed = !this.workspacesExposed; - } + this.workspacesExposed = !this.workspacesExposed; } render() { @@ -78,8 +53,7 @@ export class WorkspacesMenu extends React.Component { transition: "all 1s ease", zIndex: 15, padding: 10, - }} - > + }}> { }} onClick={this.addNewWorkspace} /> - {this.workspaceIds.map(s => + {this.props.allWorkspaces.map(s =>
  • {s}
  • + onClick={() => this.props.open(s)} + >{s.Title} )}
    ); diff --git a/src/server/database.ts b/src/server/database.ts index 07c5819ab..1553dd94e 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -70,6 +70,16 @@ export class Database { let collection = this.db.collection('documents'); let cursor = collection.find({ _id: { "$in": ids } }) cursor.toArray((err, docs) => { + if (err) { + console.log("Error"); + console.log(err.message); + console.log(err.errmsg); + console.log(ids); + console.log(["afca93a8-c6bd-4b58-967e-07784c5b12c8"]); + console.log("MAKES SENSE: " + (ids instanceof Array)); + } + console.log(typeof ids); + console.log("DATABASE: " + docs); fn(docs); }) }; diff --git a/src/server/index.ts b/src/server/index.ts index e6f08bc29..40e1f6686 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -80,60 +80,58 @@ app.use((req, res, next) => { // AUTHENTICATION ROUTING +enum Method { + Get, + Post +} + +function addSecureRoute(method: Method, + route: string, + handler: (user: DashUserModel, req: express.Request, res: express.Response) => void, + nope: (res: express.Response) => any) { + route = "/" + route; + switch (method) { + case Method.Get: + app.get(route, (req, res) => { + const dashUser: DashUserModel = req.user; + if (!dashUser) return nope(res); + handler(dashUser, req, res); + }); + break; + case Method.Post: + app.post(route, (req, res) => { + const dashUser: DashUserModel = req.user; + if (!dashUser) return nope(res); + handler(dashUser, req, res); + }); + break; + } +} + // *** // Look for the definitions of these get and post // functions in the exports of user.ts -// /home defines destination after a successful log in -app.get("/home", (req, res) => { - // if user is not logged in, redirect to log in page - const dashUser: DashUserModel = req.user; - if (!dashUser) { - return res.redirect("/login"); - } - // otherwise, connect them to Dash - // TODO: store and manage users' workspaces - // if (dashUser.allWorkspaceIds.length > 0) { - // if (!dashUser.didSelectSessionWorkspace) { - // return res.redirect("/workspaces"); - // } - // } +addSecureRoute(Method.Get, "home", (user, req, res) => { res.sendFile(path.join(__dirname, '../../deploy/index.html')); -}); - -// app.get("/workspaces", getWorkspaces); +}, res => res.redirect("/login")) -app.get("/getActiveWorkspaceId", (req, res) => { - const dashUser: DashUserModel = req.user; - if (!dashUser) { - return; - } - res.send(dashUser.activeWorkspaceId || ""); -}); +addSecureRoute(Method.Get, "getActiveWorkspaceId", (user, req, res) => { + console.log(`/getActiveWorkspaceId in index.ts ${user.activeWorkspaceId}`); + res.send(user.activeWorkspaceId || ""); +}, () => { }); -app.get("/getAllWorkspaceIds", (req, res) => { - const dashUser: DashUserModel = req.user; - if (!dashUser) { - return; - } - res.send(JSON.stringify(dashUser.allWorkspaceIds as Array)); -}) +addSecureRoute(Method.Get, "getAllWorkspaceIds", (user, req, res) => { + res.send(JSON.stringify(user.allWorkspaceIds as Array)); +}, () => { }); -app.post("/setActiveWorkspaceId", (req, res) => { - const dashUser: DashUserModel = req.user; - if (!dashUser) { - return; - } - dashUser.update({ $set: { activeWorkspaceId: req.body.target } }, () => { }); -}) +addSecureRoute(Method.Post, "setActiveWorkspaceId", (user, req) => { + user.update({ $set: { activeWorkspaceId: req.body.target } }, () => { }); +}, () => { }); -app.post("/addWorkspaceId", (req, res) => { - const dashUser: DashUserModel = req.user; - if (!dashUser) { - return; - } - dashUser.update({ $push: { allWorkspaceIds: req.body.target } }, () => { }); -}) +addSecureRoute(Method.Post, "addWorkspaceId", (user, req) => { + user.update({ $push: { allWorkspaceIds: req.body.target } }, () => { }); +}, () => { }); // anyone attempting to navigate to localhost at this port will // first have to login -- cgit v1.2.3-70-g09d2 From a8a6f2bb16173d4dc4b0690faf299e817c8937fa Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 11 Mar 2019 18:39:28 -0400 Subject: Stability improvements --- src/client/documents/Documents.ts | 9 +++++--- src/client/views/Main.tsx | 2 +- .../views/collections/CollectionFreeFormView.tsx | 26 +++++++++++----------- 3 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a735fe961..bf4acc857 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -17,6 +17,7 @@ import { KeyValueBox } from "../views/nodes/KeyValueBox" import { PDFField } from "../../fields/PDFField"; import { PDFBox } from "../views/nodes/PDFBox"; import { CollectionPDFView } from "../views/collections/CollectionPDFView"; +import { RichTextField } from "../../fields/RichTextField"; export interface DocumentOptions { x?: number; @@ -77,9 +78,11 @@ export namespace Documents { function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document { return assignOptions(new Document(protoId), { ...options, title: title, layout: layout }); } - function SetInstanceOptions(doc: Document, options: DocumentOptions, value: T, ctor: { new(): U }, id?: string) { + function SetInstanceOptions(doc: Document, options: DocumentOptions, value: T | undefined, ctor: { new(): U } | undefined, id?: string) { var deleg = doc.MakeDelegate(id); - deleg.SetData(KeyStore.Data, value, ctor); + if (value !== undefined && ctor !== undefined) { + deleg.SetData(KeyStore.Data, value, ctor); + } return assignOptions(deleg, options); } @@ -131,7 +134,7 @@ export namespace Documents { return doc; } export function TextDocument(options: DocumentOptions = {}) { - return SetInstanceOptions(GetTextPrototype(), options, "", TextField); + return SetInstanceOptions(GetTextPrototype(), options, undefined, undefined); } export function PdfDocument(url: string, options: DocumentOptions = {}) { return SetInstanceOptions(GetPdfPrototype(), options, new URL(url), PDFField); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 73d5fa8a9..44fdf60ce 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -72,7 +72,7 @@ export class Main extends React.Component { @action createNewWorkspace = (init: boolean): void => { - let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: `Main Container ${this.userWorkspaces.length}` }); + let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: `Main Container ${this.userWorkspaces.length + 1}` }); let newId = mainDoc.Id; request.post(this.contextualize("addWorkspaceId"), { body: { target: newId }, diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 11c96d074..0d5e15767 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -170,19 +170,19 @@ export class CollectionFreeFormView extends CollectionViewBase { @action onKeyDown = (e: React.KeyboardEvent) => { //if not these keys, make a textbox if preview cursor is active! - if (!e.ctrlKey && !e.altKey) { - if (this._previewCursorVisible) { - //make textbox and add it to this collection - let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY); - let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" }); - // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself - this._selectOnLoaded = newBox.Id; - //set text to be the typed key and get focus on text box - this.props.CollectionView.addDocument(newBox); - //remove cursor from screen - this._previewCursorVisible = false; - } - } + // if (!e.ctrlKey && !e.altKey) { + // if (this._previewCursorVisible) { + // //make textbox and add it to this collection + // let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY); + // let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" }); + // // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself + // this._selectOnLoaded = newBox.Id; + // //set text to be the typed key and get focus on text box + // this.props.CollectionView.addDocument(newBox); + // //remove cursor from screen + // this._previewCursorVisible = false; + // } + // } } @action -- cgit v1.2.3-70-g09d2