From 0402105238f24785a1229dbbb37f2e4dba958f88 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 10:47:28 -0500 Subject: playing with a docking view. --- src/views/collections/CollectionDockingView.scss | 393 ++++++++++++++++++++++ src/views/collections/CollectionDockingView.tsx | 154 +++++++++ src/views/collections/CollectionFreeFormView.scss | 6 + src/views/collections/CollectionFreeFormView.tsx | 207 ++++++++++++ 4 files changed, 760 insertions(+) create mode 100644 src/views/collections/CollectionDockingView.scss create mode 100644 src/views/collections/CollectionDockingView.tsx create mode 100644 src/views/collections/CollectionFreeFormView.scss create mode 100644 src/views/collections/CollectionFreeFormView.tsx (limited to 'src/views/collections') diff --git a/src/views/collections/CollectionDockingView.scss b/src/views/collections/CollectionDockingView.scss new file mode 100644 index 000000000..dc29defdc --- /dev/null +++ b/src/views/collections/CollectionDockingView.scss @@ -0,0 +1,393 @@ + +.collectiondockingview-container { + position: relative; + top: 0; + left: 0; + overflow: hidden; + .collectiondockingview { + position: relative; + top: 0; + left: 0;.flexlayout__layout { + left: 0; + top: 0; + right: 0; + bottom: 0; + position: absolute; + overflow:hidden; + } + + .flexlayout__splitter { + background-color: black; + } + + .flexlayout__splitter:hover { + background-color: #333; + } + + .flexlayout__splitter_drag { + border-radius: 5px; + background-color: #444; + z-index: 1000; + } + + .flexlayout__outline_rect { + position: absolute; + cursor: move; + border: 2px solid #cfe8ff; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + } + + .flexlayout__outline_rect_edge { + cursor: move; + border: 2px solid #b7d1b5; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + } + + .flexlayout__edge_rect { + position: absolute; + z-index: 1000; + box-shadow: inset 0 0 5px rgba(0, 0, 0, .2); + background-color: lightgray; + } + + .flexlayout__drag_rect { + position: absolute; + cursor: move; + border: 2px solid #aaaaaa; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .3); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + background-color:#eeeeee; + opacity: 0.9; + text-align: center; + + display: flex; + justify-content: center; + flex-direction: column; + overflow:hidden; + padding:10px; + word-wrap: break-word; + } + + .flexlayout__tabset { + overflow: hidden; + background-color: #222; + box-sizing: border-box; + } + + .flexlayout__tab { + overflow: auto; + position:absolute; + box-sizing: border-box; + background-color: #222; + color:white; + } + + .flexlayout__tab_button { + cursor: pointer; + padding: 2px 8px 3px 8px; + margin: 2px; + /*box-shadow: inset 0px 0px 5px rgba(0, 0, 0, .15);*/ + /*border-top-left-radius: 3px;*/ + /*border-top-right-radius: 3px;*/ + float:left; + vertical-align: top; + box-sizing: border-box; + + } + + .flexlayout__tab_button--selected { + color: #ddd; + background-color: #222; + } + + .flexlayout__tab_button--unselected { + color: gray; + } + + .flexlayout__tab_button_leading { + float: left; + display:inline-block; + } + + .flexlayout__tab_button_content { + float: left; + display:inline-block; + } + + .flexlayout__tab_button_textbox { + float: left; + border: none; + color:lightgreen; + background-color:#222; + } + .flexlayout__tab_button_textbox:focus { + outline: none; + } + + .flexlayout__tab_button_trailing { + float: left; + display:inline-block; + margin-left:5px; + margin-top:3px; + width:8px; + height:8px; + } + + .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, + .flexlayout__tab_button--selected .flexlayout__tab_button_trailing{ + //background: transparent url("../images/close_white.png") no-repeat center; + } + + .flexlayout__tab_button_overflow { + float: left; + width: 20px; + height:15px; + margin-top:2px; + padding-left:12px; + border:none; + font-size: 10px; + color:lightgray; + font-family: Arial, sans-serif; + //background: transparent url("../images/more.png") no-repeat left; + } + + .flexlayout__tabset_header + { + position: absolute; + left: 0; + right: 0; + color:#eee; + background-color: #212121; + padding:3px 3px 3px 5px; + /*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/ + box-sizing: border-box; + } + + .flexlayout__tab_header_inner { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 10000px; + } + + .flexlayout__tab_header_outer { + background-color: black; + position: absolute; + left: 0; + right: 0; + /*top: 0px;*/ + /*height: 100px;*/ + overflow: hidden; + } + + .flexlayout__tabset-selected + { + //background-image: linear-gradient(#111, #444); + } + + .flexlayout__tabset-maximized + { + //background-image: linear-gradient(#666, #333); + } + + .flexlayout__tab_toolbar { + position:absolute; + display:flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; + } + + .flexlayout__tab_toolbar_button-min { + width:20px; + height:20px; + border:none; + outline-width: 0; + //background: transparent url("../images/maximize.png") no-repeat center; + + } + .flexlayout__tab_toolbar_button-max { + width:20px; + height:20px; + border:none; + outline-width: 0; + //background: transparent url("../images/restore.png") no-repeat center; + } + + .flexlayout__popup_menu { + } + + .flexlayout__popup_menu_item { + padding: 2px 10px 2px 10px; + color:#ddd; + } + + .flexlayout__popup_menu_item:hover { + background-color: #444444; + } + + .flexlayout__popup_menu_container { + box-shadow: inset 0 0 5px rgba(0, 0, 0, .15); + border: 1px solid #555; + background: #222; + border-radius:3px; + position:absolute; + z-index:1000; + } + + + .flexlayout__border_top { + background-color:black; + border-bottom: 1px solid #ddd; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_bottom { + background-color:black; + border-top: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + .flexlayout__border_left { + background-color:black; + border-right: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_right { + background-color:black; + border-left: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_inner_bottom{ + display: flex; + } + + .flexlayout__border_inner_left { + position:absolute; + white-space: nowrap; + right: 23px; + transform-origin: top right; + transform: rotate(-90deg); + } + + .flexlayout__border_inner_right { + position:absolute; + white-space: nowrap; + left: 23px; + transform-origin: top left; + transform: rotate(90deg); + } + + .flexlayout__border_button { + background-color: #222; + color:white; + display:inline-block; + white-space:nowrap; + + cursor: pointer; + padding: 2px 8px 3px 8px; + margin: 2px; + vertical-align: top; + box-sizing: border-box; + } + + .flexlayout__border_button--selected { + color: #ddd; + background-color: #222; + } + + .flexlayout__border_button--unselected { + color: gray; + } + + .flexlayout__border_button_leading { + float: left; + display:inline; + } + + .flexlayout__border_button_content { + display:inline-block; + } + + .flexlayout__border_button_textbox { + float: left; + border: none; + color:green; + background-color:#ddd; + } + .flexlayout__border_button_textbox:focus { + outline: none; + } + + .flexlayout__border_button_trailing { + display:inline-block; + margin-left:5px; + margin-top:3px; + width:8px; + height:8px; + } + + .flexlayout__border_button:hover .flexlayout__border_button_trailing, + .flexlayout__border_button--selected .flexlayout__border_button_trailing{ + //background: transparent url("../images/close_white.png") no-repeat center; + } + + + .flexlayout__border_toolbar_left { + position:absolute; + display: flex; + flex-direction: column-reverse; + align-items: center; + bottom:0; + left:0; + right:0; + } + + .flexlayout__border_toolbar_right { + position:absolute; + display: flex; + flex-direction: column-reverse; + align-items: center; + bottom:0; + left:0; + right:0; + } + + .flexlayout__border_toolbar_top { + position:absolute; + display: flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; + } + + .flexlayout__border_toolbar_bottom { + position:absolute; + display: flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; + } + + } +} \ No newline at end of file diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx new file mode 100644 index 000000000..51323e94b --- /dev/null +++ b/src/views/collections/CollectionDockingView.tsx @@ -0,0 +1,154 @@ +import { observer } from "mobx-react"; +import { Key, KeyStore } from "../../fields/Key"; +import React = require("react"); +import FlexLayout from "flexlayout-react"; +import { action, observable, computed } from "mobx"; +import { Document } from "../../fields/Document"; +import { DocumentView, DocumentFieldViewProps, CollectionViewProps } from "../nodes/DocumentView"; +import { ListField } from "../../fields/ListField"; +import { NumberField } from "../../fields/NumberField"; +import { SSL_OP_SINGLE_DH_USE } from "constants"; +import { SelectionManager } from "../../util/SelectionManager"; +import { ContextMenu } from "../ContextMenu"; +import "./CollectionDockingView.scss" + + +@observer +export class CollectionDockingView extends React.Component { + + public static LayoutString() { return ''; } + + + private _times: number = 0; + private _containerRef = React.createRef(); + private _canvasRef = React.createRef(); + private _json = { + global: {}, + borders: [], + layout: { + "type": "row", + "weight": 100, + "children": [ + { + "type": "tabset", + "weight": 50, + "selected": 0, + "children": [ + { + "type": "tab", + "name": "CHILD #1", + "component": "grid", + } + ] + }, + { + "type": "tabset", + "weight": 50, + "selected": 0, + "children": [ + { + "type": "tab", + "name": "CHILD #2", + "component": "grid", + } + ] + } + ] + } + }; + private _model: any; + constructor(props: CollectionViewProps) { + super(props); + this._model = FlexLayout.Model.fromJson(this._json); + } + + public static BORDER_WIDTH = 2; + + @computed + public get active(): boolean { + var isSelected = (this.props.ContainingDocumentView != undefined && SelectionManager.IsSelected(this.props.ContainingDocumentView)); + var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); + var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined; + return isSelected || childSelected || topMost; + } + + componentDidMount() { + // if (this._containerRef.current) { + // DragManager.MakeDropTarget(this._containerRef.current, { + // handlers: { + // drop: this.drop + // } + // }); + // } + } + + + @action + addDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array()) + value.push(doc); + } + + @action + removeDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array()) + if (value.indexOf(doc) !== -1) { + value.splice(value.indexOf(doc), 1) + + SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems() + } + } + @action + onPointerDown = (e: React.PointerEvent): void => { + if (e.button === 2 && this.active) { + e.stopPropagation(); + e.preventDefault(); + } else { + if (e.buttons === 1 && SelectionManager.IsSelected(this.props.ContainingDocumentView!)) { + e.stopPropagation(); + } + } + } + factory = (node: any): any => { + var component = node.getComponent(); + if (component === "button") { + return ; + } + if (component === "grid") { + let which = this._times++ % 3; + if (which == 0) + return
+ if (which == 1) + return
+ if (which == 2) + return
+ } + } + + render() { + const { fieldKey, Document: Document } = this.props; + + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + const currScale: number = Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); + return ( +
+
e.preventDefault()} style={{ + width: "100%", + height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`, + }} ref={this._containerRef}> +
+ +
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss new file mode 100644 index 000000000..31e9985bc --- /dev/null +++ b/src/views/collections/CollectionFreeFormView.scss @@ -0,0 +1,6 @@ +.collectionfreeformview-container { + position: relative; + top: 0; + left: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx new file mode 100644 index 000000000..479d883d6 --- /dev/null +++ b/src/views/collections/CollectionFreeFormView.tsx @@ -0,0 +1,207 @@ +import { observer } from "mobx-react"; +import { Key, KeyStore } from "../../fields/Key"; +import React = require("react"); +import { action, observable, computed } from "mobx"; +import { Document } from "../../fields/Document"; +import { DocumentView, CollectionViewProps } from "../nodes/DocumentView"; +import { ListField } from "../../fields/ListField"; +import { NumberField } from "../../fields/NumberField"; +import { SSL_OP_SINGLE_DH_USE } from "constants"; +import { SelectionManager } from "../../util/SelectionManager"; +import { Documents } from "../../documents/Documents"; +import { ContextMenu } from "../ContextMenu"; +import { DragManager } from "../../util/DragManager"; +import "./CollectionFreeFormView.scss"; +import { Utils } from "../../Utils"; + +@observer +export class CollectionFreeFormView extends React.Component { + private _containerRef = React.createRef(); + private _canvasRef = React.createRef(); + + constructor(props: CollectionViewProps) { + super(props); + } + + public static BORDER_WIDTH = 2; + + @computed + public get active(): boolean { + var isSelected = (this.props.ContainingDocumentView != undefined && SelectionManager.IsSelected(this.props.ContainingDocumentView)); + var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); + var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined; + return isSelected || childSelected || topMost; + } + + drop = (e: Event, de: DragManager.DropEvent) => { + const ele = this._canvasRef.current; + if (!ele) { + return; + } + const doc = de.data[ "document" ]; + const xOffset = de.data[ "xOffset" ] as number || 0; + const yOffset = de.data[ "yOffset" ] as number || 0; + if (doc instanceof DocumentView) { + if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { + doc.props.ContainingCollectionView.removeDocument(doc.props.Document); + this.addDocument(doc.props.Document); + } + const { scale, translateX, translateY } = Utils.GetScreenTransform(ele); + const screenX = de.x - xOffset; + const screenY = de.y - yOffset; + const docX = (screenX - translateX) / scale; + const docY = (screenY - translateY) / scale; + doc.x = docX; + doc.y = docY; + } + e.stopPropagation(); + } + + componentDidMount() { + if (this._containerRef.current) { + DragManager.MakeDropTarget(this._containerRef.current, { + handlers: { + drop: this.drop + } + }); + } + } + + _lastX: number = 0; + _lastY: number = 0; + @action + onPointerDown = (e: React.PointerEvent): void => { + if (e.button === 2 && this.active) { + e.stopPropagation(); + e.preventDefault(); + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + this._lastX = e.pageX; + this._lastY = e.pageY; + } + } + + @action + onPointerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + } + + @action + onPointerMove = (e: PointerEvent): void => { + if (!e.cancelBubble) { + e.preventDefault(); + e.stopPropagation(); + let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace; + let x = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + let y = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + this.props.Document.SetFieldValue(KeyStore.PanX, x + (e.pageX - this._lastX) / currScale, NumberField); + this.props.Document.SetFieldValue(KeyStore.PanY, y + (e.pageY - this._lastY) / currScale, NumberField); + this._lastX = e.pageX; + this._lastY = e.pageY; + } + } + + @action + onPointerWheel = (e: React.WheelEvent): void => { + e.stopPropagation(); + + let { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY); + + var deltaScale = (1 - (e.deltaY / 1000)) * Ss; + + var newContainerX = LocalX * deltaScale + W / 2 - W / 2 * deltaScale + Panxx + Xx; + var newContainerY = LocalY * deltaScale + Panyy + Yy; + + let dx = ContainerX - newContainerX; + let dy = ContainerY - newContainerY; + + this.props.Document.SetField(KeyStore.Scale, new NumberField(deltaScale)); + this.props.Document.SetFieldValue(KeyStore.PanX, Panxx + dx, NumberField); + this.props.Document.SetFieldValue(KeyStore.PanY, Panyy + dy, NumberField); + } + + onDrop = (e: React.DragEvent): void => { + e.stopPropagation() + e.preventDefault() + let fReader = new FileReader() + let file = e.dataTransfer.items[ 0 ].getAsFile(); + let that = this; + const panx: number = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + let x = e.pageX - panx + let y = e.pageY - pany + + fReader.addEventListener("load", action("drop", (event) => { + if (fReader.result) { + let url = "" + fReader.result; + let doc = Documents.ImageDocument(url, { + x: x, y: y + }) + let docs = that.props.Document.GetFieldT(KeyStore.Data, ListField); + if (!docs) { + docs = new ListField(); + that.props.Document.SetField(KeyStore.Data, docs) + } + docs.Data.push(doc); + } + }), false) + + if (file) { + fReader.readAsDataURL(file) + } + } + + @action + addDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array()) + value.push(doc); + } + + @action + removeDocument = (doc: Document): void => { + //TODO This won't create the field if it doesn't already exist + const value = this.props.Document.GetFieldValue(this.props.fieldKey, ListField, new Array()) + if (value.indexOf(doc) !== -1) { + value.splice(value.indexOf(doc), 1) + + SelectionManager.DeselectAll() + ContextMenu.Instance.clearItems() + } + } + + onDragOver = (e: React.DragEvent): void => { + } + render() { + const { fieldKey, Document: Document } = this.props; + + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); + const pany: number = Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); + const currScale: number = Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); + return ( +
+
e.preventDefault()} style={{ + width: "100%", + height: `calc(100% - 2*${CollectionFreeFormView.BORDER_WIDTH}px)`, + }} onDrop={this.onDrop} onDragOver={this.onDragOver} ref={this._containerRef}> +
+ +
+ {value.map(doc => { + return (); + })} +
+
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ed982a553d1831353e312ae8137afa95ef84ebe5 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 12:20:13 -0500 Subject: semi working docking, but zooming is now broken. --- build/index.html | 1 - src/DocumentDecorations.tsx | 5 +- src/Main.tsx | 10 +- src/Utils.ts | 2 +- src/documents/Documents.ts | 4 +- src/views/collections/CollectionDockingView.scss | 759 +++++++++++----------- src/views/collections/CollectionDockingView.tsx | 59 +- src/views/collections/CollectionFreeFormView.scss | 7 +- src/views/nodes/DocumentView.tsx | 19 +- 9 files changed, 444 insertions(+), 422 deletions(-) (limited to 'src/views/collections') diff --git a/build/index.html b/build/index.html index 49872f02b..4027667cd 100644 --- a/build/index.html +++ b/build/index.html @@ -2,7 +2,6 @@ Dash Web - diff --git a/src/DocumentDecorations.tsx b/src/DocumentDecorations.tsx index 7899db9c9..d5a682b82 100644 --- a/src/DocumentDecorations.tsx +++ b/src/DocumentDecorations.tsx @@ -24,7 +24,10 @@ export class DocumentDecorations extends React.Component { return SelectionManager.SelectedDocuments().reduce((bounds, element) => { var spt = element.TransformToScreenPoint(0, 0); var bpt = element.TransformToScreenPoint(element.width, element.height); - return { + if (spt.ScreenX == undefined || spt.ScreenY == undefined || + bpt.ScreenX == undefined || bpt.ScreenY == undefined) + return { x: bounds.x, y: bounds.y, r: bounds.r, b: bounds.b }; + else return { x: Math.min(spt.ScreenX, bounds.x), y: Math.min(spt.ScreenY, bounds.y), r: Math.max(bpt.ScreenX, bounds.r), b: Math.max(bpt.ScreenY, bounds.b) } diff --git a/src/Main.tsx b/src/Main.tsx index 6ccb940f4..1b5d95132 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -54,14 +54,14 @@ runInAction(() => { let doc3 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { x: 450, y: 500 }); - let docset = new Array(doc1, doc2); + let docset = new Array(doc1, doc2, doc3); let doc4 = Documents.CollectionDocument(docset, { x: 0, y: 400 }); 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 }); - let doc6 = Documents.DockDocument("", { + let doc6 = Documents.DockDocument(docset, { x: 350, y: 100 }); let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField); @@ -71,8 +71,10 @@ runInAction(() => { } // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); - // mainNodes.Data.push(doc4); + mainNodes.Data.push(doc4); // mainNodes.Data.push(doc3); - // mainNodes.Data.push(doc5); + mainNodes.Data.push(doc5); + mainNodes.Data.push(doc1); + mainNodes.Data.push(doc2); mainNodes.Data.push(doc6); }); \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index 948b6176e..cc1d8f6c6 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -13,7 +13,7 @@ export class Utils { public static GetScreenTransform(ele: HTMLElement): { scale: number, translateX: number, translateY: number } { const rect = ele.getBoundingClientRect(); - const scale = rect.width / ele.offsetWidth; + const scale = ele.offsetWidth == 0 && rect.width == 0 ? 1 : rect.width / ele.offsetWidth; const translateX = rect.left; const translateY = rect.top; diff --git a/src/documents/Documents.ts b/src/documents/Documents.ts index a0bd021d8..7cc5de3a3 100644 --- a/src/documents/Documents.ts +++ b/src/documents/Documents.ts @@ -68,10 +68,10 @@ export namespace Documents { return dockProto; } - export function DockDocument(text: string, options: DocumentOptions = {}): Document { + export function DockDocument(documents: Array, options: DocumentOptions = {}): Document { let doc = GetDockPrototype().MakeDelegate(); setupOptions(doc, options); - // doc.SetField(KeyStore.Data, new TextField(text)); + doc.SetField(KeyStore.Data, new ListField(documents)); return doc; } diff --git a/src/views/collections/CollectionDockingView.scss b/src/views/collections/CollectionDockingView.scss index dc29defdc..43af7c538 100644 --- a/src/views/collections/CollectionDockingView.scss +++ b/src/views/collections/CollectionDockingView.scss @@ -4,390 +4,383 @@ top: 0; left: 0; overflow: hidden; - .collectiondockingview { + .flexlayout__layout { + width: 100%; + height: 100%; position: relative; + overflow:hidden; + } + + .flexlayout__splitter { + background-color: black; + } + + .flexlayout__splitter:hover { + background-color: #333; + } + + .flexlayout__splitter_drag { + border-radius: 5px; + background-color: #444; + z-index: 1000; + } + + .flexlayout__outline_rect { + position: absolute; + cursor: move; + border: 2px solid #cfe8ff; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + } + + .flexlayout__outline_rect_edge { + cursor: move; + border: 2px solid #b7d1b5; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + } + + .flexlayout__edge_rect { + position: absolute; + z-index: 1000; + box-shadow: inset 0 0 5px rgba(0, 0, 0, .2); + background-color: lightgray; + } + + .flexlayout__drag_rect { + position: absolute; + cursor: move; + border: 2px solid #aaaaaa; + box-shadow: inset 0 0 60px rgba(0, 0, 0, .3); + border-radius: 5px; + z-index: 1000; + box-sizing: border-box; + background-color:#eeeeee; + opacity: 0.9; + text-align: center; + + display: flex; + justify-content: center; + flex-direction: column; + overflow:hidden; + padding:10px; + word-wrap: break-word; + } + + .flexlayout__tabset { + overflow: hidden; + background-color: #222; + box-sizing: border-box; + } + + .flexlayout__tab { + overflow: auto; + position: absolute; + box-sizing: border-box; + background-color: #222; + } + + .flexlayout__tab_button { + cursor: pointer; + padding: 2px 8px 3px 8px; + margin: 2px; + /*box-shadow: inset 0px 0px 5px rgba(0, 0, 0, .15);*/ + /*border-top-left-radius: 3px;*/ + /*border-top-right-radius: 3px;*/ + float:left; + vertical-align: top; + box-sizing: border-box; + + } + + .flexlayout__tab_button--selected { + color: #ddd; + background-color: #222; + } + + .flexlayout__tab_button--unselected { + color: gray; + } + + .flexlayout__tab_button_leading { + float: left; + display:inline-block; + } + + .flexlayout__tab_button_content { + float: left; + display:inline-block; + } + + .flexlayout__tab_button_textbox { + float: left; + border: none; + color:lightgreen; + background-color:#222; + } + .flexlayout__tab_button_textbox:focus { + outline: none; + } + + .flexlayout__tab_button_trailing { + float: left; + display:inline-block; + margin-left:5px; + margin-top:3px; + width:8px; + height:8px; + } + + .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, + .flexlayout__tab_button--selected .flexlayout__tab_button_trailing{ + //background: transparent url("../images/close_white.png") no-repeat center; + } + + .flexlayout__tab_button_overflow { + float: left; + width: 20px; + height:15px; + margin-top:2px; + padding-left:12px; + border:none; + font-size: 10px; + color:lightgray; + font-family: Arial, sans-serif; + //background: transparent url("../images/more.png") no-repeat left; + } + + .flexlayout__tabset_header + { + position: absolute; + left: 0; + right: 0; + color:#eee; + background-color: #212121; + padding:3px 3px 3px 5px; + /*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/ + box-sizing: border-box; + } + + .flexlayout__tab_header_inner { + position: absolute; + left: 0; top: 0; - left: 0;.flexlayout__layout { - left: 0; - top: 0; - right: 0; - bottom: 0; - position: absolute; - overflow:hidden; - } - - .flexlayout__splitter { - background-color: black; - } - - .flexlayout__splitter:hover { - background-color: #333; - } - - .flexlayout__splitter_drag { - border-radius: 5px; - background-color: #444; - z-index: 1000; - } - - .flexlayout__outline_rect { - position: absolute; - cursor: move; - border: 2px solid #cfe8ff; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); - border-radius: 5px; - z-index: 1000; - box-sizing: border-box; - } - - .flexlayout__outline_rect_edge { - cursor: move; - border: 2px solid #b7d1b5; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); - border-radius: 5px; - z-index: 1000; - box-sizing: border-box; - } - - .flexlayout__edge_rect { - position: absolute; - z-index: 1000; - box-shadow: inset 0 0 5px rgba(0, 0, 0, .2); - background-color: lightgray; - } - - .flexlayout__drag_rect { - position: absolute; - cursor: move; - border: 2px solid #aaaaaa; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .3); - border-radius: 5px; - z-index: 1000; - box-sizing: border-box; - background-color:#eeeeee; - opacity: 0.9; - text-align: center; - - display: flex; - justify-content: center; - flex-direction: column; - overflow:hidden; - padding:10px; - word-wrap: break-word; - } - - .flexlayout__tabset { - overflow: hidden; - background-color: #222; - box-sizing: border-box; - } - - .flexlayout__tab { - overflow: auto; - position:absolute; - box-sizing: border-box; - background-color: #222; - color:white; - } - - .flexlayout__tab_button { - cursor: pointer; - padding: 2px 8px 3px 8px; - margin: 2px; - /*box-shadow: inset 0px 0px 5px rgba(0, 0, 0, .15);*/ - /*border-top-left-radius: 3px;*/ - /*border-top-right-radius: 3px;*/ - float:left; - vertical-align: top; - box-sizing: border-box; - - } - - .flexlayout__tab_button--selected { - color: #ddd; - background-color: #222; - } - - .flexlayout__tab_button--unselected { - color: gray; - } - - .flexlayout__tab_button_leading { - float: left; - display:inline-block; - } - - .flexlayout__tab_button_content { - float: left; - display:inline-block; - } - - .flexlayout__tab_button_textbox { - float: left; - border: none; - color:lightgreen; - background-color:#222; - } - .flexlayout__tab_button_textbox:focus { - outline: none; - } - - .flexlayout__tab_button_trailing { - float: left; - display:inline-block; - margin-left:5px; - margin-top:3px; - width:8px; - height:8px; - } - - .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, - .flexlayout__tab_button--selected .flexlayout__tab_button_trailing{ - //background: transparent url("../images/close_white.png") no-repeat center; - } - - .flexlayout__tab_button_overflow { - float: left; - width: 20px; - height:15px; - margin-top:2px; - padding-left:12px; - border:none; - font-size: 10px; - color:lightgray; - font-family: Arial, sans-serif; - //background: transparent url("../images/more.png") no-repeat left; - } - - .flexlayout__tabset_header - { - position: absolute; - left: 0; - right: 0; - color:#eee; - background-color: #212121; - padding:3px 3px 3px 5px; - /*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/ - box-sizing: border-box; - } - - .flexlayout__tab_header_inner { - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 10000px; - } - - .flexlayout__tab_header_outer { - background-color: black; - position: absolute; - left: 0; - right: 0; - /*top: 0px;*/ - /*height: 100px;*/ - overflow: hidden; - } - - .flexlayout__tabset-selected - { - //background-image: linear-gradient(#111, #444); - } - - .flexlayout__tabset-maximized - { - //background-image: linear-gradient(#666, #333); - } - - .flexlayout__tab_toolbar { - position:absolute; - display:flex; - flex-direction: row-reverse; - align-items: center; - top:0; - bottom:0; - right:0; - } - - .flexlayout__tab_toolbar_button-min { - width:20px; - height:20px; - border:none; - outline-width: 0; - //background: transparent url("../images/maximize.png") no-repeat center; - - } - .flexlayout__tab_toolbar_button-max { - width:20px; - height:20px; - border:none; - outline-width: 0; - //background: transparent url("../images/restore.png") no-repeat center; - } - - .flexlayout__popup_menu { - } - - .flexlayout__popup_menu_item { - padding: 2px 10px 2px 10px; - color:#ddd; - } - - .flexlayout__popup_menu_item:hover { - background-color: #444444; - } - - .flexlayout__popup_menu_container { - box-shadow: inset 0 0 5px rgba(0, 0, 0, .15); - border: 1px solid #555; - background: #222; - border-radius:3px; - position:absolute; - z-index:1000; - } - - - .flexlayout__border_top { - background-color:black; - border-bottom: 1px solid #ddd; - box-sizing: border-box; - overflow:hidden; - } - - .flexlayout__border_bottom { - background-color:black; - border-top: 1px solid #333; - box-sizing: border-box; - overflow:hidden; - } - .flexlayout__border_left { - background-color:black; - border-right: 1px solid #333; - box-sizing: border-box; - overflow:hidden; - } - - .flexlayout__border_right { - background-color:black; - border-left: 1px solid #333; - box-sizing: border-box; - overflow:hidden; - } - - .flexlayout__border_inner_bottom{ - display: flex; - } - - .flexlayout__border_inner_left { - position:absolute; - white-space: nowrap; - right: 23px; - transform-origin: top right; - transform: rotate(-90deg); - } - - .flexlayout__border_inner_right { - position:absolute; - white-space: nowrap; - left: 23px; - transform-origin: top left; - transform: rotate(90deg); - } - - .flexlayout__border_button { - background-color: #222; - color:white; - display:inline-block; - white-space:nowrap; - - cursor: pointer; - padding: 2px 8px 3px 8px; - margin: 2px; - vertical-align: top; - box-sizing: border-box; - } - - .flexlayout__border_button--selected { - color: #ddd; - background-color: #222; - } - - .flexlayout__border_button--unselected { - color: gray; - } - - .flexlayout__border_button_leading { - float: left; - display:inline; - } - - .flexlayout__border_button_content { - display:inline-block; - } - - .flexlayout__border_button_textbox { - float: left; - border: none; - color:green; - background-color:#ddd; - } - .flexlayout__border_button_textbox:focus { - outline: none; - } - - .flexlayout__border_button_trailing { - display:inline-block; - margin-left:5px; - margin-top:3px; - width:8px; - height:8px; - } - - .flexlayout__border_button:hover .flexlayout__border_button_trailing, - .flexlayout__border_button--selected .flexlayout__border_button_trailing{ - //background: transparent url("../images/close_white.png") no-repeat center; - } - - - .flexlayout__border_toolbar_left { - position:absolute; - display: flex; - flex-direction: column-reverse; - align-items: center; - bottom:0; - left:0; - right:0; - } - - .flexlayout__border_toolbar_right { - position:absolute; - display: flex; - flex-direction: column-reverse; - align-items: center; - bottom:0; - left:0; - right:0; - } - - .flexlayout__border_toolbar_top { - position:absolute; - display: flex; - flex-direction: row-reverse; - align-items: center; - top:0; - bottom:0; - right:0; - } - - .flexlayout__border_toolbar_bottom { - position:absolute; - display: flex; - flex-direction: row-reverse; - align-items: center; - top:0; - bottom:0; - right:0; - } - + bottom: 0; + width: 10000px; + } + + .flexlayout__tab_header_outer { + background-color: black; + position: absolute; + left: 0; + right: 0; + /*top: 0px;*/ + /*height: 100px;*/ + overflow: hidden; + } + + .flexlayout__tabset-selected + { + //background-image: linear-gradient(#111, #444); + } + + .flexlayout__tabset-maximized + { + //background-image: linear-gradient(#666, #333); + } + + .flexlayout__tab_toolbar { + position:absolute; + display:flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; + } + + .flexlayout__tab_toolbar_button-min { + width:20px; + height:20px; + border:none; + outline-width: 0; + //background: transparent url("../images/maximize.png") no-repeat center; + + } + .flexlayout__tab_toolbar_button-max { + width:20px; + height:20px; + border:none; + outline-width: 0; + //background: transparent url("../images/restore.png") no-repeat center; + } + + .flexlayout__popup_menu { + } + + .flexlayout__popup_menu_item { + padding: 2px 10px 2px 10px; + color:#ddd; + } + + .flexlayout__popup_menu_item:hover { + background-color: #444444; + } + + .flexlayout__popup_menu_container { + box-shadow: inset 0 0 5px rgba(0, 0, 0, .15); + border: 1px solid #555; + background: #222; + border-radius:3px; + position:absolute; + z-index:1000; + } + + + .flexlayout__border_top { + background-color:black; + border-bottom: 1px solid #ddd; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_bottom { + background-color:black; + border-top: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + .flexlayout__border_left { + background-color:black; + border-right: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_right { + background-color:black; + border-left: 1px solid #333; + box-sizing: border-box; + overflow:hidden; + } + + .flexlayout__border_inner_bottom{ + display: flex; + } + + .flexlayout__border_inner_left { + position:absolute; + white-space: nowrap; + right: 23px; + transform-origin: top right; + transform: rotate(-90deg); + } + + .flexlayout__border_inner_right { + position:absolute; + white-space: nowrap; + left: 23px; + transform-origin: top left; + transform: rotate(90deg); + } + + .flexlayout__border_button { + background-color: #222; + color:white; + display:inline-block; + white-space:nowrap; + + cursor: pointer; + padding: 2px 8px 3px 8px; + margin: 2px; + vertical-align: top; + box-sizing: border-box; + } + + .flexlayout__border_button--selected { + color: #ddd; + background-color: #222; + } + + .flexlayout__border_button--unselected { + color: gray; + } + + .flexlayout__border_button_leading { + float: left; + display:inline; + } + + .flexlayout__border_button_content { + display:inline-block; + } + + .flexlayout__border_button_textbox { + float: left; + border: none; + color:green; + background-color:#ddd; + } + .flexlayout__border_button_textbox:focus { + outline: none; + } + + .flexlayout__border_button_trailing { + display:inline-block; + margin-left:5px; + margin-top:3px; + width:8px; + height:8px; + } + + .flexlayout__border_button:hover .flexlayout__border_button_trailing, + .flexlayout__border_button--selected .flexlayout__border_button_trailing{ + //background: transparent url("../images/close_white.png") no-repeat center; + } + + + .flexlayout__border_toolbar_left { + position:absolute; + display: flex; + flex-direction: column-reverse; + align-items: center; + bottom:0; + left:0; + right:0; + } + + .flexlayout__border_toolbar_right { + position:absolute; + display: flex; + flex-direction: column-reverse; + align-items: center; + bottom:0; + left:0; + right:0; + } + + .flexlayout__border_toolbar_top { + position:absolute; + display: flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; + } + + .flexlayout__border_toolbar_bottom { + position:absolute; + display: flex; + flex-direction: row-reverse; + align-items: center; + top:0; + bottom:0; + right:0; } + } \ No newline at end of file diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index 51323e94b..b6fff6ba0 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -37,7 +37,7 @@ export class CollectionDockingView extends React.Component { "type": "tab", "name": "CHILD #1", - "component": "grid", + "component": "doc1", } ] }, @@ -49,7 +49,19 @@ export class CollectionDockingView extends React.Component { "type": "tab", "name": "CHILD #2", - "component": "grid", + "component": "doc2", + } + ] + }, + { + "type": "tabset", + "weight": 50, + "selected": 0, + "children": [ + { + "type": "tab", + "name": "CHILD #3", + "component": "doc3", } ] } @@ -107,7 +119,7 @@ export class CollectionDockingView extends React.Component e.stopPropagation(); e.preventDefault(); } else { - if (e.buttons === 1 && SelectionManager.IsSelected(this.props.ContainingDocumentView!)) { + if (e.buttons === 1 && this.active) { e.stopPropagation(); } } @@ -117,14 +129,20 @@ export class CollectionDockingView extends React.Component if (component === "button") { return ; } - if (component === "grid") { - let which = this._times++ % 3; - if (which == 0) - return
- if (which == 1) - return
- if (which == 2) - return
+ console.log("Gettting " + component); + const { fieldKey, Document: Document } = this.props; + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + if (component === "doc1") { + return (); + } + if (component === "doc2") { + return (); + } + if (component === "doc3") { + return (); + } + if (component === "text") { + return (
Panel {node.getName()}
); } } @@ -132,21 +150,20 @@ export class CollectionDockingView extends React.Component const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); - const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); - const pany: number = Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); - const currScale: number = Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); + // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes. + var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); + var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)); return (
-
e.preventDefault()} style={{ - width: "100%", - height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`, - }} ref={this._containerRef}> -
- -
+
e.preventDefault()} ref={this._containerRef} + style={{ + width: "100%", + height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`, + }} > +
); diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss index 31e9985bc..2b68ba981 100644 --- a/src/views/collections/CollectionFreeFormView.scss +++ b/src/views/collections/CollectionFreeFormView.scss @@ -1,6 +1,11 @@ .collectionfreeformview-container { - position: relative; + position: absolute; top: 0; left: 0; overflow: hidden; + .collectionfreeformview { + position: absolute; + top: 0; + left: 0; + } } \ No newline at end of file diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 3ee70213f..517d691f9 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -15,6 +15,7 @@ import { DocumentDecorations } from "../../DocumentDecorations"; import { ContextMenu } from "../ContextMenu"; import { Opt } from "../../fields/Field"; import { DragManager } from "../../util/DragManager"; +import { number } from "prop-types"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? interface DocumentViewProps { @@ -193,14 +194,16 @@ export class DocumentView extends React.Component { // // Converts a point in the coordinate space of a document to a screen space coordinate. // - public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: number, ScreenY: number } { - + public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: Opt, ScreenY: Opt } { + if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { + return { ScreenX: undefined, ScreenY: undefined }; + } let W = this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let H = CollectionFreeFormView.BORDER_WIDTH; let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); - let parentX = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2; - let parentY = (localY - H) * Ss + (Yy + Panyy) + H; + let parentX: Opt = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2; + let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; // if this collection view is nested within another collection view, then // first transform the local point into the parent collection's coordinate space. @@ -295,11 +298,12 @@ export class DocumentView extends React.Component { } render() { + let freeStyling = this.props.ContainingCollectionView instanceof CollectionFreeFormView; return (
@@ -307,5 +311,4 @@ export class DocumentView extends React.Component {
); } - } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 7bc8bf62d9c9ff8e388103d0faa75855d43b8e16 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 12:55:59 -0500 Subject: fixed zooming --- src/Main.tsx | 7 ++-- src/views/collections/CollectionFreeFormView.scss | 4 +-- src/views/nodes/DocumentView.tsx | 42 +++++++++++++++-------- 3 files changed, 33 insertions(+), 20 deletions(-) (limited to 'src/views/collections') diff --git a/src/Main.tsx b/src/Main.tsx index 1b5d95132..566216373 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -61,7 +61,8 @@ runInAction(() => { 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 }); - let doc6 = Documents.DockDocument(docset, { + let docset2 = new Array(doc1, doc4, doc3); + let doc6 = Documents.DockDocument(docset2, { x: 350, y: 100 }); let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField); @@ -74,7 +75,7 @@ runInAction(() => { mainNodes.Data.push(doc4); // mainNodes.Data.push(doc3); mainNodes.Data.push(doc5); - mainNodes.Data.push(doc1); - mainNodes.Data.push(doc2); + // mainNodes.Data.push(doc1); + // mainNodes.Data.push(doc2); mainNodes.Data.push(doc6); }); \ No newline at end of file diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss index 2b68ba981..1563712fb 100644 --- a/src/views/collections/CollectionFreeFormView.scss +++ b/src/views/collections/CollectionFreeFormView.scss @@ -1,10 +1,10 @@ .collectionfreeformview-container { - position: absolute; + position: relative; top: 0; left: 0; overflow: hidden; .collectionfreeformview { - position: absolute; + position: relative; top: 0; left: 0; } diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 517d691f9..3b9e6cc04 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -15,7 +15,6 @@ import { DocumentDecorations } from "../../DocumentDecorations"; import { ContextMenu } from "../ContextMenu"; import { Opt } from "../../fields/Field"; import { DragManager } from "../../util/DragManager"; -import { number } from "prop-types"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? interface DocumentViewProps { @@ -202,8 +201,8 @@ export class DocumentView extends React.Component { let H = CollectionFreeFormView.BORDER_WIDTH; let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); - let parentX: Opt = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2; - let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; + let parentX: Opt = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2; + let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; // if this collection view is nested within another collection view, then // first transform the local point into the parent collection's coordinate space. @@ -297,18 +296,31 @@ export class DocumentView extends React.Component { } } + render() { - let freeStyling = this.props.ContainingCollectionView instanceof CollectionFreeFormView; - return ( -
- -
- ); + var freestyling = this.props.ContainingCollectionView === undefined || this.props.ContainingCollectionView instanceof CollectionFreeFormView; + if (freestyling) + return ( +
+ +
+ ); + else + return ( +
+ +
+ ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 0a1264837da6de1bd73637307cc9c52678efa20f Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 13:07:33 -0500 Subject: cleaned up zooming --- src/views/collections/CollectionFreeFormView.scss | 2 +- src/views/collections/CollectionFreeFormView.tsx | 4 +- src/views/nodes/DocumentView.tsx | 45 ++++++++--------------- 3 files changed, 19 insertions(+), 32 deletions(-) (limited to 'src/views/collections') diff --git a/src/views/collections/CollectionFreeFormView.scss b/src/views/collections/CollectionFreeFormView.scss index 1563712fb..870e48556 100644 --- a/src/views/collections/CollectionFreeFormView.scss +++ b/src/views/collections/CollectionFreeFormView.scss @@ -4,7 +4,7 @@ left: 0; overflow: hidden; .collectionfreeformview { - position: relative; + position: absolute; top: 0; left: 0; } diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx index 479d883d6..a1224f4da 100644 --- a/src/views/collections/CollectionFreeFormView.tsx +++ b/src/views/collections/CollectionFreeFormView.tsx @@ -109,11 +109,11 @@ export class CollectionFreeFormView extends React.Component onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); - let { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY); + let { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.props.ContainingDocumentView!.TransformToLocalPoint(e.pageX, e.pageY); var deltaScale = (1 - (e.deltaY / 1000)) * Ss; - var newContainerX = LocalX * deltaScale + W / 2 - W / 2 * deltaScale + Panxx + Xx; + var newContainerX = LocalX * deltaScale + Panxx + Xx; var newContainerY = LocalY * deltaScale + Panyy + Yy; let dx = ContainerX - newContainerX; diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 3b9e6cc04..0df97d62a 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -167,7 +167,7 @@ export class DocumentView extends React.Component { // Converts a coordinate in the screen space of the app into a local document coordinate. // public TransformToLocalPoint(screenX: number, screenY: number) { - let ContainerX = screenX; + let ContainerX = screenX - CollectionFreeFormView.BORDER_WIDTH; let ContainerY = screenY - CollectionFreeFormView.BORDER_WIDTH; // if this collection view is nested within another collection view, then @@ -178,16 +178,15 @@ export class DocumentView extends React.Component { ContainerY = LocalY - CollectionFreeFormView.BORDER_WIDTH; } - let W = this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); let Ss = this.props.Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); let Panxx = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); let Panyy = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); - let LocalX = (ContainerX - (Xx + Panxx) - W / 2) / Ss + W / 2; + let LocalX = (ContainerX - (Xx + Panxx)) / Ss; let LocalY = (ContainerY - (Yy + Panyy)) / Ss; - return { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY }; + return { LocalX, Ss, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY }; } // @@ -197,11 +196,11 @@ export class DocumentView extends React.Component { if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { return { ScreenX: undefined, ScreenY: undefined }; } - let W = this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); + let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let H = CollectionFreeFormView.BORDER_WIDTH; let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); - let parentX: Opt = (localX - W / 2) * Ss + (Xx + Panxx) + W / 2; + let parentX: Opt = (localX - W) * Ss + (Xx + Panxx) + W; let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; // if this collection view is nested within another collection view, then @@ -299,28 +298,16 @@ export class DocumentView extends React.Component { render() { var freestyling = this.props.ContainingCollectionView === undefined || this.props.ContainingCollectionView instanceof CollectionFreeFormView; - if (freestyling) - return ( -
- -
- ); - else - return ( -
- -
- ); + return ( +
+ +
+ ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From d8ff5b1effe0db563defc2a6e1391b9b0d160e67 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 14:43:42 -0500 Subject: intermediate state -- things don't resize properly yet. --- src/DocumentDecorations.tsx | 2 -- src/views/collections/CollectionDockingView.tsx | 6 +++--- src/views/collections/CollectionFreeFormView.tsx | 18 +++++------------- src/views/nodes/DocumentView.tsx | 6 +++--- 4 files changed, 11 insertions(+), 21 deletions(-) (limited to 'src/views/collections') diff --git a/src/DocumentDecorations.tsx b/src/DocumentDecorations.tsx index d5a682b82..d71cda539 100644 --- a/src/DocumentDecorations.tsx +++ b/src/DocumentDecorations.tsx @@ -1,10 +1,8 @@ import { observable, computed } from "mobx"; import React = require("react"); -import { DocumentView } from "./views/nodes/DocumentView"; import { SelectionManager } from "./util/SelectionManager"; import { observer } from "mobx-react"; import './DocumentDecorations.scss' -import { CollectionFreeFormView } from "./views/collections/CollectionFreeFormView"; @observer export class DocumentDecorations extends React.Component { diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index b6fff6ba0..a59a40b33 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -132,13 +132,13 @@ export class CollectionDockingView extends React.Component console.log("Gettting " + component); const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); - if (component === "doc1") { + if (component === "doc1" && value.length > 0) { return (); } - if (component === "doc2") { + if (component === "doc2" && value.length > 1) { return (); } - if (component === "doc3") { + if (component === "doc3" && value.length > 2) { return (); } if (component === "text") { diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx index a1224f4da..c84b8e3e5 100644 --- a/src/views/collections/CollectionFreeFormView.tsx +++ b/src/views/collections/CollectionFreeFormView.tsx @@ -34,25 +34,17 @@ export class CollectionFreeFormView extends React.Component } drop = (e: Event, de: DragManager.DropEvent) => { - const ele = this._canvasRef.current; - if (!ele) { - return; - } const doc = de.data[ "document" ]; - const xOffset = de.data[ "xOffset" ] as number || 0; - const yOffset = de.data[ "yOffset" ] as number || 0; if (doc instanceof DocumentView) { if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { doc.props.ContainingCollectionView.removeDocument(doc.props.Document); this.addDocument(doc.props.Document); } - const { scale, translateX, translateY } = Utils.GetScreenTransform(ele); - const screenX = de.x - xOffset; - const screenY = de.y - yOffset; - const docX = (screenX - translateX) / scale; - const docY = (screenY - translateY) / scale; - doc.x = docX; - doc.y = docY; + const xOffset = de.data[ "xOffset" ] as number || 0; + const yOffset = de.data[ "yOffset" ] as number || 0; + let { LocalX, LocalY } = this.props.ContainingDocumentView!.TransformToLocalPoint(de.x - xOffset, de.y - yOffset); + doc.x = LocalX; + doc.y = LocalY; } e.stopPropagation(); } diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 0df97d62a..5ce64b347 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -193,9 +193,9 @@ export class DocumentView extends React.Component { // Converts a point in the coordinate space of a document to a screen space coordinate. // public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: Opt, ScreenY: Opt } { - if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { - return { ScreenX: undefined, ScreenY: undefined }; - } + // if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { + // return { ScreenX: undefined, ScreenY: undefined }; + // } let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let H = CollectionFreeFormView.BORDER_WIDTH; let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); -- cgit v1.2.3-70-g09d2 From c45dd584af76e1cd6e48fa44f9296228cdceb649 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 29 Jan 2019 15:00:18 -0500 Subject: started looking at golden layout... --- package-lock.json | 23 +++++++++++++++++++++++ package.json | 2 ++ src/views/collections/CollectionDockingView.tsx | 4 +++- 3 files changed, 28 insertions(+), 1 deletion(-) (limited to 'src/views/collections') diff --git a/package-lock.json b/package-lock.json index 28f612e0a..b04a31ccd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3123,6 +3123,14 @@ "minimatch": "3.0.4" } }, + "golden-layout": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz", + "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=", + "requires": { + "jquery": "3.3.1" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -3696,6 +3704,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jquery": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + }, "js-base64": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz", @@ -8272,6 +8285,16 @@ "scheduler": "0.12.0" } }, + "react-golden-layout": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-golden-layout/-/react-golden-layout-1.0.6.tgz", + "integrity": "sha512-KZQ17Bnd+LfyCqe2scVMznrGKTciX3VwoT3y4xn3Qok9hknCvVXZfXe2RSX5zNG7FlLJzWt0VWqy8qZBHpQVuQ==", + "requires": { + "golden-layout": "1.5.9", + "react": "16.7.0", + "react-dom": "16.7.0" + } + }, "react-is": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz", diff --git a/package.json b/package.json index e898873e2..f0ab9ab35 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@types/prosemirror-view": "^1.3.0", "@types/uuid": "^3.4.4", "flexlayout-react": "^0.3.3", + "golden-layout": "^1.5.9", "mobx": "^5.5.2", "mobx-react": "^5.3.5", "mobx-react-devtools": "^6.0.3", @@ -54,6 +55,7 @@ "prosemirror-view": "^1.6.8", "react": "^16.5.2", "react-dom": "^16.7.0", + "react-golden-layout": "^1.0.6", "react-jsx-parser": "^1.13.0", "uuid": "^3.3.2" } diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index a59a40b33..cdcdf3bf1 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -11,7 +11,9 @@ import { SSL_OP_SINGLE_DH_USE } from "constants"; import { SelectionManager } from "../../util/SelectionManager"; import { ContextMenu } from "../ContextMenu"; import "./CollectionDockingView.scss" - +import 'golden-layout/src/css/goldenlayout-base.css'; +import 'golden-layout/src/css/goldenlayout-dark-theme.css'; +// import GoldenLayout, { Row, Stack, createGoldenLayoutComponent } from '../../../node_modules/react-golden-layout/src/internal/'; @observer export class CollectionDockingView extends React.Component { -- cgit v1.2.3-70-g09d2 From 02891812b01888aba3eada58d6051a80a79c1a18 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 29 Jan 2019 21:48:52 -0500 Subject: flexLayout fixes, but flexlayout seems hopelessly broken once it gets scaled. --- src/Main.tsx | 2 +- src/util/DragManager.ts | 23 ++++++++++++++--------- src/views/collections/CollectionDockingView.tsx | 12 +++++++----- src/views/nodes/DocumentView.tsx | 10 ++++++---- 4 files changed, 28 insertions(+), 19 deletions(-) (limited to 'src/views/collections') diff --git a/src/Main.tsx b/src/Main.tsx index 566216373..c636ee42e 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -61,7 +61,7 @@ runInAction(() => { 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 }); - let docset2 = new Array(doc1, doc4, doc3); + let docset2 = new Array(doc4, doc1, doc3); let doc6 = Documents.DockDocument(docset2, { x: 350, y: 100 }); diff --git a/src/util/DragManager.ts b/src/util/DragManager.ts index e523408a3..ca5b6c0ea 100644 --- a/src/util/DragManager.ts +++ b/src/util/DragManager.ts @@ -1,7 +1,7 @@ -import {Opt} from "../fields/Field"; -import {DocumentView} from "../views/nodes/DocumentView"; -import {DocumentDecorations} from "../DocumentDecorations"; -import {SelectionManager} from "./SelectionManager"; +import { Opt } from "../fields/Field"; +import { DocumentView } from "../views/nodes/DocumentView"; +import { DocumentDecorations } from "../DocumentDecorations"; +import { SelectionManager } from "./SelectionManager"; export namespace DragManager { export let rootId = "root"; @@ -33,7 +33,7 @@ export namespace DragManager { } export class DropEvent { - constructor(readonly x: number, readonly y: number, readonly data: {[ id: string ]: any}) {} + constructor(readonly x: number, readonly y: number, readonly data: { [ id: string ]: any }) { } } export interface DropHandlers { @@ -56,7 +56,10 @@ export namespace DragManager { }; } - export function StartDrag(ele: HTMLElement, dragData: {[ id: string ]: any}, options: DragOptions) { + + let _lastPointerX: number = 0; + let _lastPointerY: number = 0; + export function StartDrag(ele: HTMLElement, dragData: { [ id: string ]: any }, options: DragOptions) { if (!dragDiv) { const root = document.getElementById(rootId); if (!root) { @@ -76,6 +79,8 @@ export namespace DragManager { dragElement.style.transformOrigin = "0 0"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; dragDiv.appendChild(dragElement); + _lastPointerX = dragData[ "xOffset" ] + rect.left; + _lastPointerY = dragData[ "yOffset" ] + rect.top; let hideSource = false; if (typeof options.hideSource === "boolean") { @@ -91,8 +96,8 @@ export namespace DragManager { const moveHandler = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - x += e.movementX; - y += e.movementY; + x += e.clientX - _lastPointerX; _lastPointerX = e.clientX; + y += e.clientY - _lastPointerY; _lastPointerY = e.clientY; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; }; const upHandler = (e: PointerEvent) => { @@ -107,7 +112,7 @@ export namespace DragManager { document.addEventListener("pointerup", upHandler); } - function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: {[ index: string ]: any}) { + function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: { [ index: string ]: any }) { dragDiv.removeChild(dragEle); const target = document.elementFromPoint(e.x, e.y); if (!target) { diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index cdcdf3bf1..19b212bde 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -77,6 +77,7 @@ export class CollectionDockingView extends React.Component } public static BORDER_WIDTH = 2; + public static TAB_HEADER_HEIGHT = 20; @computed public get active(): boolean { @@ -131,7 +132,6 @@ export class CollectionDockingView extends React.Component if (component === "button") { return ; } - console.log("Gettting " + component); const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); if (component === "doc1" && value.length > 0) { @@ -153,8 +153,10 @@ export class CollectionDockingView extends React.Component const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes. - var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); - var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)); + var s = this.props.ContainingDocumentView!.ScalingToScreenSpace; + var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s; + var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s; + return (
}}>
e.preventDefault()} ref={this._containerRef} style={{ - width: "100%", - height: `calc(100% - 2*${CollectionDockingView.BORDER_WIDTH}px)`, + width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH, + height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH }} >
diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 5ce64b347..7ba62e0b8 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -178,8 +178,9 @@ export class DocumentView extends React.Component { ContainerY = LocalY - CollectionFreeFormView.BORDER_WIDTH; } - let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); - let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); + let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView; + let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); + let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); let Ss = this.props.Document.GetFieldValue(KeyStore.Scale, NumberField, Number(1)); let Panxx = this.props.Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); let Panyy = this.props.Document.GetFieldValue(KeyStore.PanY, NumberField, Number(0)); @@ -196,10 +197,11 @@ export class DocumentView extends React.Component { // if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { // return { ScreenX: undefined, ScreenY: undefined }; // } + let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView; let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let H = CollectionFreeFormView.BORDER_WIDTH; - let Xx = this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); - let Yy = this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); + let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); + let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); let parentX: Opt = (localX - W) * Ss + (Xx + Panxx) + W; let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; -- cgit v1.2.3-70-g09d2 From 3032decb31fa6c891520923ed49612ba5ecf7c93 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 30 Jan 2019 02:30:15 -0500 Subject: added golden layout. doesn't work much better than the flex layout. --- package-lock.json | 43 +++++++++ package.json | 2 + src/Main.tsx | 9 +- src/views/collections/CollectionDockingView.tsx | 112 +++++++++++++++++++++--- src/views/nodes/DocumentView.tsx | 5 +- 5 files changed, 155 insertions(+), 16 deletions(-) (limited to 'src/views/collections') diff --git a/package-lock.json b/package-lock.json index b04a31ccd..192a10f9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,14 @@ "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", "dev": true }, + "@types/jquery": { + "version": "3.3.29", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.29.tgz", + "integrity": "sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ==", + "requires": { + "@types/sizzle": "2.3.2" + } + }, "@types/mocha": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz", @@ -136,6 +144,11 @@ "@types/react": "16.7.17" } }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==" + }, "@types/uuid": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", @@ -4331,6 +4344,11 @@ "to-regex": "3.0.2" } }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -8314,6 +8332,15 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-mosaic": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/react-mosaic/-/react-mosaic-0.0.20.tgz", + "integrity": "sha1-pSSr8uzyi5r2sh1NNQ/veCLvMJ4=", + "requires": { + "prop-types": "15.6.2", + "threads": "0.8.1" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -9490,6 +9517,22 @@ } } }, + "threads": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/threads/-/threads-0.8.1.tgz", + "integrity": "sha1-40ARW1lHMW0vfuMSPEwsW/nHbXI=", + "requires": { + "eventemitter3": "2.0.3", + "native-promise-only": "0.8.1" + }, + "dependencies": { + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" + } + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", diff --git a/package.json b/package.json index f0ab9ab35..3dfa158d8 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.12", + "@types/jquery": "^3.3.29", "@types/prosemirror-commands": "^1.0.1", "@types/prosemirror-history": "^1.0.1", "@types/prosemirror-keymap": "^1.0.1", @@ -57,6 +58,7 @@ "react-dom": "^16.7.0", "react-golden-layout": "^1.0.6", "react-jsx-parser": "^1.13.0", + "react-mosaic": "0.0.20", "uuid": "^3.3.2" } } diff --git a/src/Main.tsx b/src/Main.tsx index c636ee42e..118e745cd 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -44,7 +44,8 @@ ReactDOM.render(( -
), document.getElementById('root')); +
), + document.getElementById('root')); runInAction(() => { let doc1 = Documents.TextDocument("Hello world"); @@ -63,7 +64,7 @@ runInAction(() => { }); let docset2 = new Array(doc4, doc1, doc3); let doc6 = Documents.DockDocument(docset2, { - x: 350, y: 100 + x: 350, y: 100, width: 600, height: 600 }); let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField); if (!mainNodes) { @@ -72,9 +73,9 @@ runInAction(() => { } // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); - mainNodes.Data.push(doc4); + //mainNodes.Data.push(doc4); // mainNodes.Data.push(doc3); - mainNodes.Data.push(doc5); + //mainNodes.Data.push(doc5); // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); mainNodes.Data.push(doc6); diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index 19b212bde..8dabe4490 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -13,7 +13,9 @@ import { ContextMenu } from "../ContextMenu"; import "./CollectionDockingView.scss" import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -// import GoldenLayout, { Row, Stack, createGoldenLayoutComponent } from '../../../node_modules/react-golden-layout/src/internal/'; +import * as GoldenLayout from "golden-layout"; +import { CollectionFreeFormView } from './CollectionFreeFormView'; +import * as ReactDOM from 'react-dom'; @observer export class CollectionDockingView extends React.Component { @@ -87,15 +89,86 @@ export class CollectionDockingView extends React.Component return isSelected || childSelected || topMost; } + myLayout: any = null; componentDidMount() { - // if (this._containerRef.current) { - // DragManager.MakeDropTarget(this._containerRef.current, { - // handlers: { - // drop: this.drop - // } - // }); - // } + + const { fieldKey, Document: Document } = this.props; + + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + if (this._containerRef.current) { + if (this.myLayout == null) { + this.myLayout = new GoldenLayout({ + content: [ { + type: 'row', + content: [ { + type: 'component', + componentName: 'documentViewComponent', + componentState: { x: 0 } + }, { + type: 'component', + componentName: 'documentViewComponent', + componentState: { x: 1 } + } ] + } ] + }); + + this.myLayout.on('stackCreated', function (stack: any) { + stack + .header + .controlsContainer + .find('.lm_close') //get the close icon + .off('click') //unbind the current click handler + .click(function () { + //add your own + if (confirm('really close this?')) { + stack.remove(); + } + }); + }); + + this.myLayout.on('tabCreated', function (tab: any) { + tab + .closeElement + .off('click') //unbind the current click handler + .click(function () { + //add your own + if (confirm('really close this?')) { + tab.contentItem.remove(); + } + }); + }); + + var me = this; + var cv = this.props.ContainingDocumentView; + this.myLayout.registerComponent('documentViewComponent', this.registerComponentWithCallback); + + this.myLayout.container = this._containerRef.current; + this.myLayout.init(); + } + } } + private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })(); + + private registerComponentWithCallback = (container: any, state: any) => { + const { fieldKey, Document: Document } = this.props; + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + var containingDiv = "component_" + this.nextId(); + container.getElement().html("
"); + // var new_state = Object.assign({}, state); + // new_state[ "location" ] = containingDiv; + // container.setState(new_state); + var me = this; + var docToRender = value[ state.x ]; + setTimeout(function () { + ReactDOM.render(( +
+ +
+ ), + document.getElementById(containingDiv) + ) + }, 1); + }; @action @@ -148,7 +221,9 @@ export class CollectionDockingView extends React.Component } } + render() { + const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); @@ -157,6 +232,8 @@ export class CollectionDockingView extends React.Component var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s; var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s; + + return (
}}>
e.preventDefault()} ref={this._containerRef} style={{ - width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH, - height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH + width: "100%", + height: "100%" }} > -
); + // return ( + //
+ //
e.preventDefault()} ref={this._containerRef} + // style={{ + // width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH, + // height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH + // }} > + // + //
+ //
+ // ); } } \ No newline at end of file diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 7ba62e0b8..38e695ed2 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -93,6 +93,9 @@ export class DocumentView extends React.Component { private _downX: number = 0; private _downY: number = 0; + constructor(props: DocumentViewProps) { + super(props); + } get screenRect(): ClientRect | DOMRect { if (this._mainCont.current) { return this._mainCont.current.getBoundingClientRect(); @@ -223,7 +226,7 @@ export class DocumentView extends React.Component { this._downX = e.clientX; this._downY = e.clientY; this._contextMenuCanOpen = e.button == 2; - if (this.active) { + if (this.active && !e.isDefaultPrevented()) { e.stopPropagation(); if (e.buttons === 2) { e.preventDefault(); -- cgit v1.2.3-70-g09d2 From 8ce38637c0a8ebb8aab936aec2ee46d5833e7b3b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 30 Jan 2019 02:35:37 -0500 Subject: from last from last --- src/views/collections/CollectionDockingView.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/views/collections') diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index 8dabe4490..7dfec1c54 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -90,7 +90,7 @@ export class CollectionDockingView extends React.Component } myLayout: any = null; - componentDidMount() { + componentDidMount: () => void = () => { const { fieldKey, Document: Document } = this.props; @@ -138,10 +138,7 @@ export class CollectionDockingView extends React.Component }); }); - var me = this; - var cv = this.props.ContainingDocumentView; this.myLayout.registerComponent('documentViewComponent', this.registerComponentWithCallback); - this.myLayout.container = this._containerRef.current; this.myLayout.init(); } -- cgit v1.2.3-70-g09d2 From 13518146c955f012a6f6cd2b802f80aeeffcd58d Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 30 Jan 2019 09:45:15 -0500 Subject: docking for top-level only --- src/DocumentDecorations.tsx | 10 ++++++---- src/Main.tsx | 8 ++++---- src/views/collections/CollectionFreeFormView.tsx | 5 ++++- src/views/nodes/DocumentView.tsx | 10 ++++------ 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'src/views/collections') diff --git a/src/DocumentDecorations.tsx b/src/DocumentDecorations.tsx index d71cda539..1cf875ea5 100644 --- a/src/DocumentDecorations.tsx +++ b/src/DocumentDecorations.tsx @@ -3,6 +3,7 @@ import React = require("react"); import { SelectionManager } from "./util/SelectionManager"; import { observer } from "mobx-react"; import './DocumentDecorations.scss' +import { CollectionFreeFormView } from "./views/collections/CollectionFreeFormView"; @observer export class DocumentDecorations extends React.Component { @@ -20,12 +21,13 @@ export class DocumentDecorations extends React.Component { @computed get Bounds(): { x: number, y: number, b: number, r: number } { return SelectionManager.SelectedDocuments().reduce((bounds, element) => { + if (element.props.ContainingCollectionView != undefined && + !(element.props.ContainingCollectionView instanceof CollectionFreeFormView)) { + return bounds; + } var spt = element.TransformToScreenPoint(0, 0); var bpt = element.TransformToScreenPoint(element.width, element.height); - if (spt.ScreenX == undefined || spt.ScreenY == undefined || - bpt.ScreenX == undefined || bpt.ScreenY == undefined) - return { x: bounds.x, y: bounds.y, r: bounds.r, b: bounds.b }; - else return { + return { x: Math.min(spt.ScreenX, bounds.x), y: Math.min(spt.ScreenY, bounds.y), r: Math.max(bpt.ScreenX, bounds.r), b: Math.max(bpt.ScreenY, bounds.b) } diff --git a/src/Main.tsx b/src/Main.tsx index 118e745cd..33e38004f 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -22,7 +22,7 @@ configure({ }); const mainNodeCollection = new Array(); -let mainContainer = Documents.CollectionDocument(mainNodeCollection, { +let mainContainer = Documents.DockDocument(mainNodeCollection, { x: 0, y: 0, width: window.screen.width, height: window.screen.height }) @@ -73,10 +73,10 @@ runInAction(() => { } // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); - //mainNodes.Data.push(doc4); + mainNodes.Data.push(doc4); // mainNodes.Data.push(doc3); - //mainNodes.Data.push(doc5); + mainNodes.Data.push(doc5); // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); - mainNodes.Data.push(doc6); + //mainNodes.Data.push(doc6); }); \ No newline at end of file diff --git a/src/views/collections/CollectionFreeFormView.tsx b/src/views/collections/CollectionFreeFormView.tsx index c84b8e3e5..736bcb786 100644 --- a/src/views/collections/CollectionFreeFormView.tsx +++ b/src/views/collections/CollectionFreeFormView.tsx @@ -13,6 +13,7 @@ import { ContextMenu } from "../ContextMenu"; import { DragManager } from "../../util/DragManager"; import "./CollectionFreeFormView.scss"; import { Utils } from "../../Utils"; +import { CollectionDockingView } from "./CollectionDockingView"; @observer export class CollectionFreeFormView extends React.Component { @@ -29,7 +30,9 @@ export class CollectionFreeFormView extends React.Component public get active(): boolean { var isSelected = (this.props.ContainingDocumentView != undefined && SelectionManager.IsSelected(this.props.ContainingDocumentView)); var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined; + var topMost = this.props.ContainingDocumentView != undefined && ( + this.props.ContainingDocumentView.props.ContainingCollectionView == undefined || + this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView); return isSelected || childSelected || topMost; } diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 38e695ed2..86d5ed305 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -196,17 +196,15 @@ export class DocumentView extends React.Component { // // Converts a point in the coordinate space of a document to a screen space coordinate. // - public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: Opt, ScreenY: Opt } { - // if (this.props.ContainingCollectionView != undefined && !(this.props.ContainingCollectionView instanceof CollectionFreeFormView)) { - // return { ScreenX: undefined, ScreenY: undefined }; - // } + public TransformToScreenPoint(localX: number, localY: number, Ss: number = 1, Panxx: number = 0, Panyy: number = 0): { ScreenX: number, ScreenY: number } { + let dockingViewChromeHack = this.props.ContainingCollectionView instanceof CollectionDockingView; let W = CollectionFreeFormView.BORDER_WIDTH; // this.props.Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)); let H = CollectionFreeFormView.BORDER_WIDTH; let Xx = dockingViewChromeHack ? 0 : this.props.Document.GetFieldValue(KeyStore.X, NumberField, Number(0)); let Yy = dockingViewChromeHack ? CollectionDockingView.TAB_HEADER_HEIGHT : this.props.Document.GetFieldValue(KeyStore.Y, NumberField, Number(0)); - let parentX: Opt = (localX - W) * Ss + (Xx + Panxx) + W; - let parentY: Opt = (localY - H) * Ss + (Yy + Panyy) + H; + let parentX = (localX - W) * Ss + (Xx + Panxx) + W; + let parentY = (localY - H) * Ss + (Yy + Panyy) + H; // if this collection view is nested within another collection view, then // first transform the local point into the parent collection's coordinate space. -- cgit v1.2.3-70-g09d2 From 022b8a3a562a30062f714f9ce7082b8983a89513 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 30 Jan 2019 10:29:59 -0500 Subject: cleaned up docking a little --- src/Main.tsx | 39 +++++++++++++------------ src/documents/Documents.ts | 4 +++ src/fields/Key.ts | 1 + src/views/collections/CollectionDockingView.tsx | 30 ++++++++----------- 4 files changed, 38 insertions(+), 36 deletions(-) (limited to 'src/views/collections') diff --git a/src/Main.tsx b/src/Main.tsx index 33e38004f..02b866753 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -23,7 +23,7 @@ configure({ const mainNodeCollection = new Array(); let mainContainer = Documents.DockDocument(mainNodeCollection, { - x: 0, y: 0, width: window.screen.width, height: window.screen.height + x: 0, y: 0, width: window.screen.width, height: window.screen.height, title: "main container" }) window.addEventListener("drop", function (e) { @@ -38,17 +38,10 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { } }), true) -ReactDOM.render(( -
-

Dash Web

- - - -
), - document.getElementById('root')); -runInAction(() => { - let doc1 = Documents.TextDocument("Hello world"); +//runInAction(() => +{ + let doc1 = Documents.TextDocument("Hello world", { title: "hello" }); let doc2 = doc1.MakeDelegate(); doc2.SetField(KS.X, new NumberField(150)); doc2.SetField(KS.Y, new NumberField(20)); @@ -57,19 +50,18 @@ runInAction(() => { }); let docset = new Array(doc1, doc2, doc3); let doc4 = Documents.CollectionDocument(docset, { - x: 0, y: 400 + 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 + x: 650, y: 500, width: 600, height: 600, title: "cat" }); let docset2 = new Array(doc4, doc1, doc3); let doc6 = Documents.DockDocument(docset2, { - x: 350, y: 100, width: 600, height: 600 + x: 350, y: 100, width: 600, height: 600, title: "docking collection" }); - let mainNodes = mainContainer.GetFieldT(KeyStore.Data, ListField); + let mainNodes = null;// mainContainer.GetFieldT(KeyStore.Data, ListField); if (!mainNodes) { mainNodes = new ListField(); - mainContainer.SetField(KeyStore.Data, mainNodes); } // mainNodes.Data.push(doc1); // mainNodes.Data.push(doc2); @@ -77,6 +69,17 @@ runInAction(() => { // mainNodes.Data.push(doc3); mainNodes.Data.push(doc5); // mainNodes.Data.push(doc1); - // mainNodes.Data.push(doc2); + mainNodes.Data.push(doc2); //mainNodes.Data.push(doc6); -}); \ No newline at end of file + mainContainer.SetField(KeyStore.Data, mainNodes); +} +//); + +ReactDOM.render(( +
+

Dash Web

+ + + +
), + document.getElementById('root')); \ No newline at end of file diff --git a/src/documents/Documents.ts b/src/documents/Documents.ts index 7cc5de3a3..318a6d7a3 100644 --- a/src/documents/Documents.ts +++ b/src/documents/Documents.ts @@ -11,6 +11,7 @@ interface DocumentOptions { y?: number; width?: number; height?: number; + title?: string; } export namespace Documents { @@ -27,6 +28,9 @@ export namespace Documents { if (options.height) { doc.SetFieldValue(KeyStore.Height, options.height, NumberField); } + if (options.title) { + doc.SetFieldValue(KeyStore.Title, options.title, TextField); + } doc.SetFieldValue(KeyStore.Scale, 1, NumberField); doc.SetFieldValue(KeyStore.PanX, 0, NumberField); doc.SetFieldValue(KeyStore.PanY, 0, NumberField); diff --git a/src/fields/Key.ts b/src/fields/Key.ts index f8418d5c0..61b3cdd37 100644 --- a/src/fields/Key.ts +++ b/src/fields/Key.ts @@ -34,6 +34,7 @@ export namespace KeyStore { export let Prototype = new Key("Prototype"); export let X = new Key("X"); export let Y = new Key("Y"); + export let Title = new Key("Title"); export let PanX = new Key("PanX"); export let PanY = new Key("PanY"); export let Scale = new Key("Scale"); diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index 7dfec1c54..e07e53168 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -16,6 +16,7 @@ import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import * as GoldenLayout from "golden-layout"; import { CollectionFreeFormView } from './CollectionFreeFormView'; import * as ReactDOM from 'react-dom'; +import { TextField } from "../../fields/TextField"; @observer export class CollectionDockingView extends React.Component { @@ -97,18 +98,14 @@ export class CollectionDockingView extends React.Component const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); if (this._containerRef.current) { if (this.myLayout == null) { + var docs = value.map(doc => { + var d = { fcomponent: undefined, type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } }; + return d; + }); this.myLayout = new GoldenLayout({ content: [ { type: 'row', - content: [ { - type: 'component', - componentName: 'documentViewComponent', - componentState: { x: 0 } - }, { - type: 'component', - componentName: 'documentViewComponent', - componentState: { x: 1 } - } ] + content: docs } ] }); @@ -127,6 +124,9 @@ export class CollectionDockingView extends React.Component }); this.myLayout.on('tabCreated', function (tab: any) { + var docToRender = tab.contentItem.config.componentState.doc; + var dflt: string = ""; + tab.setTitle(docToRender.GetFieldValue(KeyStore.Title, TextField, dflt)); tab .closeElement .off('click') //unbind the current click handler @@ -147,15 +147,11 @@ export class CollectionDockingView extends React.Component private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })(); private registerComponentWithCallback = (container: any, state: any) => { - const { fieldKey, Document: Document } = this.props; - const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); var containingDiv = "component_" + this.nextId(); container.getElement().html("
"); - // var new_state = Object.assign({}, state); - // new_state[ "location" ] = containingDiv; - // container.setState(new_state); var me = this; - var docToRender = value[ state.x ]; + var docToRender = state.doc; + // bcz: ugly way to do this. wait until containoing div has been created, then look it up in the DOM and render our document view into it. setTimeout(function () { ReactDOM.render((
@@ -164,7 +160,7 @@ export class CollectionDockingView extends React.Component ), document.getElementById(containingDiv) ) - }, 1); + }, 0); }; @@ -229,8 +225,6 @@ export class CollectionDockingView extends React.Component var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s; var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s; - - return (
Date: Wed, 30 Jan 2019 11:12:52 -0500 Subject: cleaned up so that its easy to switch between flexLayout and GoldenLayout --- src/fields/Document.ts | 7 +- src/views/collections/CollectionDockingView.tsx | 271 ++++++++++-------------- src/views/nodes/DocumentView.tsx | 1 - 3 files changed, 115 insertions(+), 164 deletions(-) (limited to 'src/views/collections') diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 6b1cccaf9..9580ab5c0 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -1,10 +1,15 @@ import { Field, Cast, Opt } from "./Field" import { Key, KeyStore } from "./Key" -import { ObservableMap } from "mobx"; +import { ObservableMap, computed } from "mobx"; +import { TextField } from "./TextField"; export class Document extends Field { private fields: ObservableMap = new ObservableMap(); + static _untitledDocName = ""; + @computed + public get Title() { return this.GetFieldValue(KeyStore.Title, TextField, Document._untitledDocName); } + GetField(key: Key, ignoreProto: boolean = false): Opt { let field: Opt; if (ignoreProto) { diff --git a/src/views/collections/CollectionDockingView.tsx b/src/views/collections/CollectionDockingView.tsx index e07e53168..fd492503d 100644 --- a/src/views/collections/CollectionDockingView.tsx +++ b/src/views/collections/CollectionDockingView.tsx @@ -1,10 +1,10 @@ import { observer } from "mobx-react"; -import { Key, KeyStore } from "../../fields/Key"; +import { KeyStore } from "../../fields/Key"; import React = require("react"); import FlexLayout from "flexlayout-react"; import { action, observable, computed } from "mobx"; import { Document } from "../../fields/Document"; -import { DocumentView, DocumentFieldViewProps, CollectionViewProps } from "../nodes/DocumentView"; +import { DocumentView, CollectionViewProps } from "../nodes/DocumentView"; import { ListField } from "../../fields/ListField"; import { NumberField } from "../../fields/NumberField"; import { SSL_OP_SINGLE_DH_USE } from "constants"; @@ -14,69 +14,42 @@ import "./CollectionDockingView.scss" import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import * as GoldenLayout from "golden-layout"; -import { CollectionFreeFormView } from './CollectionFreeFormView'; import * as ReactDOM from 'react-dom'; -import { TextField } from "../../fields/TextField"; @observer export class CollectionDockingView extends React.Component { + private static UseGoldenLayout = true; public static LayoutString() { return ''; } - - - private _times: number = 0; private _containerRef = React.createRef(); - private _canvasRef = React.createRef(); - private _json = { - global: {}, - borders: [], - layout: { - "type": "row", - "weight": 100, - "children": [ - { - "type": "tabset", - "weight": 50, - "selected": 0, - "children": [ - { - "type": "tab", - "name": "CHILD #1", - "component": "doc1", - } - ] - }, - { - "type": "tabset", - "weight": 50, - "selected": 0, - "children": [ - { - "type": "tab", - "name": "CHILD #2", - "component": "doc2", - } - ] - }, - { - "type": "tabset", - "weight": 50, - "selected": 0, - "children": [ - { - "type": "tab", - "name": "CHILD #3", - "component": "doc3", - } - ] - } - ] - } - }; - private _model: any; + @computed + private get modelForFlexLayout() { + const { fieldKey, Document: Document } = this.props; + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + var docs = value.map(doc => { + return { type: 'tabset', weight: 50, selected: 0, children: [ { type: "tab", name: doc.Title, component: doc.Id } ] }; + }); + return FlexLayout.Model.fromJson({ + global: {}, borders: [], + layout: { + "type": "row", + "weight": 100, + "children": docs + } + }); + } + @computed + private get modelForGoldenLayout(): any { + const { fieldKey, Document: Document } = this.props; + const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); + var docs = value.map(doc => { + var d = { type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } }; + return d; + }); + return new GoldenLayout({ content: [ { type: 'row', content: docs } ] }); + } constructor(props: CollectionViewProps) { super(props); - this._model = FlexLayout.Model.fromJson(this._json); } public static BORDER_WIDTH = 2; @@ -90,80 +63,13 @@ export class CollectionDockingView extends React.Component return isSelected || childSelected || topMost; } - myLayout: any = null; componentDidMount: () => void = () => { - - const { fieldKey, Document: Document } = this.props; - - const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); - if (this._containerRef.current) { - if (this.myLayout == null) { - var docs = value.map(doc => { - var d = { fcomponent: undefined, type: 'component', componentName: 'documentViewComponent', componentState: { doc: doc } }; - return d; - }); - this.myLayout = new GoldenLayout({ - content: [ { - type: 'row', - content: docs - } ] - }); - - this.myLayout.on('stackCreated', function (stack: any) { - stack - .header - .controlsContainer - .find('.lm_close') //get the close icon - .off('click') //unbind the current click handler - .click(function () { - //add your own - if (confirm('really close this?')) { - stack.remove(); - } - }); - }); - - this.myLayout.on('tabCreated', function (tab: any) { - var docToRender = tab.contentItem.config.componentState.doc; - var dflt: string = ""; - tab.setTitle(docToRender.GetFieldValue(KeyStore.Title, TextField, dflt)); - tab - .closeElement - .off('click') //unbind the current click handler - .click(function () { - //add your own - if (confirm('really close this?')) { - tab.contentItem.remove(); - } - }); - }); - - this.myLayout.registerComponent('documentViewComponent', this.registerComponentWithCallback); - this.myLayout.container = this._containerRef.current; - this.myLayout.init(); - } + if (this._containerRef.current && CollectionDockingView.UseGoldenLayout) { + this.goldenLayoutFactory(); } } private nextId = (function () { var _next_id = 0; return function () { return _next_id++; } })(); - private registerComponentWithCallback = (container: any, state: any) => { - var containingDiv = "component_" + this.nextId(); - container.getElement().html("
"); - var me = this; - var docToRender = state.doc; - // bcz: ugly way to do this. wait until containoing div has been created, then look it up in the DOM and render our document view into it. - setTimeout(function () { - ReactDOM.render(( -
- -
- ), - document.getElementById(containingDiv) - ) - }, 0); - }; - - @action addDocument = (doc: Document): void => { //TODO This won't create the field if it doesn't already exist @@ -193,64 +99,105 @@ export class CollectionDockingView extends React.Component } } } - factory = (node: any): any => { + + flexLayoutFactory = (node: any): any => { var component = node.getComponent(); if (component === "button") { return ; } const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); - if (component === "doc1" && value.length > 0) { - return (); - } - if (component === "doc2" && value.length > 1) { - return (); - } - if (component === "doc3" && value.length > 2) { - return (); + for (var i: number = 0; i < value.length; i++) { + if (value[ i ].Id === component) { + return (); + } } if (component === "text") { return (
Panel {node.getName()}
); } } + goldenLayoutFactory() { + var myLayout = this.modelForGoldenLayout; - render() { + myLayout.on('stackCreated', function (stack: any) { + 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(); + } + }); + }); + + myLayout.on('tabCreated', function (tab: any) { + tab.setTitle(tab.contentItem.config.componentState.doc.Title); + tab.closeElement.off('click') //unbind the current click handler + .click(function () { + if (confirm('really close this?')) { + tab.contentItem.remove(); + } + }); + }); + + var me = this; + 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 () { + ReactDOM.render(( + + ), + document.getElementById(containingDiv) + ) + }, 0); + }); + myLayout.container = this._containerRef.current; + myLayout.init(); + } - const { fieldKey, Document: Document } = this.props; + render() { + const { fieldKey, Document: Document } = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); // bcz: not sure why, but I need these to force the flexlayout to update when the collection size changes. var s = this.props.ContainingDocumentView!.ScalingToScreenSpace; var w = Document.GetFieldValue(KeyStore.Width, NumberField, Number(0)) / s; var h = Document.GetFieldValue(KeyStore.Height, NumberField, Number(0)) / s; - return ( -
-
e.preventDefault()} ref={this._containerRef} - style={{ - width: "100%", - height: "100%" - }} > + if (CollectionDockingView.UseGoldenLayout) { + return ( +
+
e.preventDefault()} ref={this._containerRef} + style={{ + width: "100%", + height: "100%" + }} > +
-
- ); - // return ( - //
- //
e.preventDefault()} ref={this._containerRef} - // style={{ - // width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH, - // height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH - // }} > - // - //
- //
- // ); + ); + } else { + return ( +
+
e.preventDefault()} ref={this._containerRef} + style={{ + width: s > 1 ? "100%" : w - 2 * CollectionDockingView.BORDER_WIDTH, + height: s > 1 ? "100%" : h - 2 * CollectionDockingView.BORDER_WIDTH + }} > + +
+
+ ); + } } } \ No newline at end of file diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 86d5ed305..1e4cc1cca 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -11,7 +11,6 @@ import { CollectionFreeFormView } from "../collections/CollectionFreeFormView" import { CollectionDockingView } from "../collections/CollectionDockingView" import "./NodeView.scss" import { SelectionManager } from "../../util/SelectionManager"; -import { DocumentDecorations } from "../../DocumentDecorations"; import { ContextMenu } from "../ContextMenu"; import { Opt } from "../../fields/Field"; import { DragManager } from "../../util/DragManager"; -- cgit v1.2.3-70-g09d2