diff options
author | bob <bcz@cs.brown.edu> | 2019-01-28 11:21:27 -0500 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2019-01-28 11:21:27 -0500 |
commit | f69c9b50f9103ad09bb68fe4087f4165877ebaeb (patch) | |
tree | 7faf75fde4062a1db06f91c3fd3fa91b79ef7b9c | |
parent | db241485f4f9dff0f43cf5e8059ccb7bd8df5b15 (diff) |
fixed pan interactions. simplified dragging (I think).
-rw-r--r-- | src/util/DragManager.ts | 68 | ||||
-rw-r--r-- | src/views/freeformcanvas/CollectionFreeFormView.tsx | 59 | ||||
-rw-r--r-- | src/views/nodes/DocumentView.tsx | 110 |
3 files changed, 95 insertions, 142 deletions
diff --git a/src/util/DragManager.ts b/src/util/DragManager.ts index 89cf45199..da4537845 100644 --- a/src/util/DragManager.ts +++ b/src/util/DragManager.ts @@ -1,4 +1,7 @@ -import { Opt } from "../fields/Field"; +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"; @@ -20,20 +23,10 @@ export namespace DragManager { (): void; } - export class DragStartEvent { - private _cancelled: boolean = false; - get cancelled() { return this._cancelled }; - - cancel() { this._cancelled = true; }; - - constructor(readonly x: number, readonly y: number, readonly data: { [id: string]: any }) { } - } - export class DragCompleteEvent { } export interface DragHandlers { - dragStart: (e: DragStartEvent) => void; dragComplete: (e: DragCompleteEvent) => void; } @@ -42,48 +35,18 @@ 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 { drop: (e: Event, de: DropEvent) => void; } - export function MakeDraggable(element: HTMLElement, options: DragOptions): DragDropDisposer { - if ("draggable" in element.dataset) { - throw new Error("Element is already draggable, can't make it draggable again"); - } - element.dataset["draggable"] = "true"; - const dispose = () => { - document.removeEventListener("pointerup", upHandler); - document.removeEventListener("pointermove", startDragHandler); - } - const startDragHandler = (e: PointerEvent) => { - e.stopImmediatePropagation(); - e.preventDefault(); - dispose(); - StartDrag(element, e, options); - } - const upHandler = (e: PointerEvent) => { - dispose(); - }; - const downHandler = (e: PointerEvent) => { - document.addEventListener("pointermove", startDragHandler); - document.addEventListener("pointerup", upHandler); - }; - element.addEventListener("pointerdown", downHandler); - - return () => { - element.removeEventListener("pointerdown", downHandler); - delete element.dataset["draggable"]; - } - } - export function MakeDropTarget(element: HTMLElement, options: DropOptions): DragDropDisposer { - if ("draggable" in element.dataset) { + if ("canDrop" in element.dataset) { throw new Error("Element is already droppable, can't make it droppable again"); } - element.dataset["canDrop"] = "true"; + element.dataset[ "canDrop" ] = "true"; const handler = (e: Event) => { const ce = e as CustomEvent<DropEvent>; options.handlers.drop(e, ce.detail); @@ -91,11 +54,11 @@ export namespace DragManager { element.addEventListener("dashOnDrop", handler); return () => { element.removeEventListener("dashOnDrop", handler); - delete element.dataset["canDrop"] + delete element.dataset[ "canDrop" ] }; } - function StartDrag(ele: HTMLElement, e: PointerEvent, options: DragOptions) { + export function StartDrag(ele: HTMLElement, dragData: {[ id: string ]: any}, options: DragOptions) { if (!dragDiv) { const root = document.getElementById(rootId); if (!root) { @@ -104,17 +67,6 @@ export namespace DragManager { dragDiv = document.createElement("div"); root.appendChild(dragDiv); } - if ((e.buttons & options.buttons) === 0) { - return; - } - e.stopPropagation(); - e.preventDefault(); - let dragData = {}; - let event = new DragStartEvent(e.x, e.y, dragData); - options.handlers.dragStart(event); - if (event.cancelled) { - return; - } const w = ele.offsetWidth, h = ele.offsetHeight; const rect = ele.getBoundingClientRect(); const scaleX = rect.width / w, scaleY = rect.height / h; @@ -157,7 +109,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/freeformcanvas/CollectionFreeFormView.tsx b/src/views/freeformcanvas/CollectionFreeFormView.tsx index f3ee2da23..fc473f0a6 100644 --- a/src/views/freeformcanvas/CollectionFreeFormView.tsx +++ b/src/views/freeformcanvas/CollectionFreeFormView.tsx @@ -1,20 +1,20 @@ -import { observer } from "mobx-react"; -import { Key, KeyStore } from "../../fields/Key"; +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 { DocumentViewModel } from "../../viewmodels/DocumentViewModel"; -import { DocumentView } from "../nodes/DocumentView"; -import { ListField } from "../../fields/ListField"; -import { NumberField } from "../../fields/NumberField"; -import { SSL_OP_SINGLE_DH_USE } from "constants"; -import { DocumentDecorations } from "../../DocumentDecorations"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Documents } from "../../documents/Documents"; -import { ContextMenu } from "../ContextMenu"; -import { Opt } from "../../fields/Field"; -import { DragManager } from "../../util/DragManager"; -import { Utils } from "../../Utils"; +import {action, observable, computed} from "mobx"; +import {Document} from "../../fields/Document"; +import {DocumentViewModel} from "../../viewmodels/DocumentViewModel"; +import {DocumentView} from "../nodes/DocumentView"; +import {ListField} from "../../fields/ListField"; +import {NumberField} from "../../fields/NumberField"; +import {SSL_OP_SINGLE_DH_USE} from "constants"; +import {DocumentDecorations} from "../../DocumentDecorations"; +import {SelectionManager} from "../../util/SelectionManager"; +import {Documents} from "../../documents/Documents"; +import {ContextMenu} from "../ContextMenu"; +import {Opt} from "../../fields/Field"; +import {DragManager} from "../../util/DragManager"; +import {Utils} from "../../Utils"; interface IProps { fieldKey: Key; @@ -44,15 +44,15 @@ export class CollectionFreeFormView extends React.Component<IProps> { if (!ele) { return; } - const doc = de.data["document"]; - const xOffset = de.data["xOffset"] as number || 0; - const yOffset = de.data["yOffset"] as number || 0; + 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 {scale, translateX, translateY} = Utils.GetScreenTransform(ele); const screenX = de.x - xOffset; const screenY = de.y - yOffset; const docX = (screenX - translateX) / scale; @@ -61,7 +61,7 @@ export class CollectionFreeFormView extends React.Component<IProps> { doc.y = docY; } e.stopPropagation(); - + DocumentDecorations.Instance.forceUpdate(); } componentDidMount() { @@ -95,11 +95,6 @@ export class CollectionFreeFormView extends React.Component<IProps> { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); - - var topMost = this.props.ContainingDocumentView != undefined && this.props.ContainingDocumentView.props.ContainingCollectionView == undefined; - if (topMost) { - SelectionManager.DeselectAll() - } } @action @@ -131,7 +126,7 @@ export class CollectionFreeFormView extends React.Component<IProps> { let ContainerY = inputY; if (me.props.ContainingDocumentView != undefined) { let pme = me.props.ContainingDocumentView!; - let { LocalX, LocalY } = this.getLocalPoint(pme, ContainerX, ContainerY); + let {LocalX, LocalY} = this.getLocalPoint(pme, ContainerX, ContainerY); ContainerX = LocalX; ContainerY = LocalY; } @@ -145,14 +140,14 @@ export class CollectionFreeFormView extends React.Component<IProps> { let LocalX = W / 2 - (Xx + Panxx) / Ss + (ContainerX - W / 2) / Ss; let LocalY = -(Yy + Panyy) / Ss + ContainerY / Ss; - return { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY }; + return {LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY}; } @action onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); - let { LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY } = this.getLocalPoint(this.props.ContainingDocumentView!, e.pageX, e.pageY); + let {LocalX, Ss, W, Panxx, Xx, LocalY, Panyy, Yy, ContainerX, ContainerY} = this.getLocalPoint(this.props.ContainingDocumentView!, e.pageX, e.pageY); var deltaScale = (1 - (e.deltaY / 1000)) * Ss; @@ -173,7 +168,7 @@ export class CollectionFreeFormView extends React.Component<IProps> { e.stopPropagation() e.preventDefault() let fReader = new FileReader() - let file = e.dataTransfer.items[0].getAsFile(); + 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)); @@ -222,7 +217,7 @@ export class CollectionFreeFormView extends React.Component<IProps> { onDragOver = (e: React.DragEvent): void => { } render() { - const { fieldKey, Document: Document } = this.props; + const {fieldKey, Document: Document} = this.props; const value: Document[] = Document.GetFieldValue(fieldKey, ListField, []); const panx: number = Document.GetFieldValue(KeyStore.PanX, NumberField, Number(0)); @@ -239,7 +234,7 @@ export class CollectionFreeFormView extends React.Component<IProps> { height: "calc(100% - 4px)", overflow: "hidden" }} onDrop={this.onDrop} onDragOver={this.onDragOver} ref={this._containerRef}> - <div className="collectionfreeformview" style={{ transform: `translate(${panx}px, ${pany}px) scale(${currScale}, ${currScale})`, transformOrigin: `left, top` }} ref={this._canvasRef}> + <div className="collectionfreeformview" style={{transform: `translate(${panx}px, ${pany}px) scale(${currScale}, ${currScale})`, transformOrigin: `left, top`}} ref={this._canvasRef}> <div className="node-container"> {value.map(doc => { diff --git a/src/views/nodes/DocumentView.tsx b/src/views/nodes/DocumentView.tsx index 602d28d56..da2768bbe 100644 --- a/src/views/nodes/DocumentView.tsx +++ b/src/views/nodes/DocumentView.tsx @@ -1,20 +1,20 @@ -import { observer } from "mobx-react"; +import {observer} from "mobx-react"; import React = require("react"); -import { computed, observable, action } from "mobx"; -import { KeyStore, Key } from "../../fields/Key"; -import { NumberField } from "../../fields/NumberField"; -import { TextField } from "../../fields/TextField"; -import { DocumentViewModel } from "../../viewmodels/DocumentViewModel"; -import { ListField } from "../../fields/ListField"; -import { FieldTextBox } from "../nodes/FieldTextBox" -import { Document } from "../../fields/Document"; -import { CollectionFreeFormView } from "../freeformcanvas/CollectionFreeFormView" +import {computed, observable, action} from "mobx"; +import {KeyStore, Key} from "../../fields/Key"; +import {NumberField} from "../../fields/NumberField"; +import {TextField} from "../../fields/TextField"; +import {DocumentViewModel} from "../../viewmodels/DocumentViewModel"; +import {ListField} from "../../fields/ListField"; +import {FieldTextBox} from "../nodes/FieldTextBox" +import {Document} from "../../fields/Document"; +import {CollectionFreeFormView} from "../freeformcanvas/CollectionFreeFormView" 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"; +import {SelectionManager} from "../../util/SelectionManager"; +import {DocumentDecorations} from "../../DocumentDecorations"; +import {ContextMenu} from "../ContextMenu"; +import {Opt} from "../../fields/Field"; +import {DragManager} from "../../util/DragManager"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? interface IProps { @@ -42,22 +42,22 @@ class DocumentContents extends React.Component<IProps> { } render() { let doc = this.props.Document; - let bindings = { ...this.props } as any; + let bindings = {...this.props} as any; for (const key of this.layoutKeys) { - bindings[key.Name + "Key"] = key; + bindings[ key.Name + "Key" ] = key; } for (const key of this.layoutFields) { let field = doc.GetField(key); if (field) { - bindings[key.Name] = field.GetValue(); + bindings[ key.Name ] = field.GetValue(); } } return <JsxParser - components={{ FieldTextBox, CollectionFreeFormView }} + components={{FieldTextBox, CollectionFreeFormView}} bindings={bindings} jsx={this.layout} showWarnings={true} - onError={(test: any) => { console.log(test) }} + onError={(test: any) => {console.log(test)}} /> @@ -117,56 +117,59 @@ export class DocumentView extends React.Component<IProps> { set height(h: number) { this.props.Document.SetFieldValue(KeyStore.Height, h, NumberField) } - - @action - dragStarted = (e: DragManager.DragStartEvent) => { - this._contextMenuCanOpen = false; - if (!this.props.ContainingCollectionView) { - e.cancel(); - return; - } - const rect = this.screenRect; - e.data["document"] = this; - e.data["xOffset"] = e.x - rect.left; - e.data["yOffset"] = e.y - rect.top; - } - @action dragComplete = (e: DragManager.DragCompleteEvent) => { } - componentDidMount() { - if (this._mainCont.current) { - DragManager.MakeDraggable(this._mainCont.current, { - buttons: 2, - handlers: { - dragComplete: this.dragComplete, - dragStart: this.dragStarted - }, - hideSource: true - }) - } - } - @computed get active(): boolean { - return SelectionManager.IsSelected(this) || (this.props.ContainingCollectionView !== undefined && this.props.ContainingCollectionView.active); + return SelectionManager.IsSelected(this) || this.props.ContainingCollectionView === undefined || this.props.ContainingCollectionView!.active; } private _downX: number = 0; private _downY: number = 0; onPointerDown = (e: React.PointerEvent): void => { - e.stopPropagation(); + let me = this; this._downX = e.clientX; this._downY = e.clientY; this._contextMenuCanOpen = e.button == 2; - document.addEventListener("pointerup", this.onPointerUp); + if (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); + } + } + + onPointerMove = (e: PointerEvent): void => { + if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { + if (this._mainCont.current != null && this.props.ContainingCollectionView != null) { + this._contextMenuCanOpen = false; + const rect = this.screenRect; + let dragData: {[ id: string ]: any} = {}; + dragData[ "document" ] = this; + dragData[ "xOffset" ] = e.x - rect.left; + dragData[ "yOffset" ] = e.y - rect.top; + DragManager.StartDrag(this._mainCont.current, dragData, { + buttons: 2, + handlers: { + dragComplete: this.dragComplete, + }, + hideSource: true + }) + } + } + e.stopPropagation(); + e.preventDefault(); } onPointerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onPointerMove) document.removeEventListener("pointerup", this.onPointerUp) e.stopPropagation(); - if ((e.clientX - this._downX) == 0 && (e.clientY - this._downY) == 0) { + if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) { SelectionManager.SelectDoc(this, e.ctrlKey); } } @@ -179,6 +182,9 @@ export class DocumentView extends React.Component<IProps> { @action onContextMenu = (e: React.MouseEvent): void => { + if (!SelectionManager.IsSelected(this)) { + return; + } e.preventDefault() if (!this._contextMenuCanOpen) { @@ -194,7 +200,7 @@ export class DocumentView extends React.Component<IProps> { e.stopPropagation(); ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) + ContextMenu.Instance.addItem({description: "Delete", event: this.deleteClicked}) ContextMenu.Instance.displayMenu(e.pageX, e.pageY) SelectionManager.SelectDoc(this, e.ctrlKey); } |