diff --git a/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts b/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts index fe88ef966..0d569e8dc 100644 --- a/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts +++ b/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, TouchEvent } from 'react'; import { SelectingState, State, @@ -45,6 +45,16 @@ export class DefaultState extends State { }) ); + // touch drags the canvas + this.registerAction( + new Action({ + type: InputType.TOUCH_START, + fire: (event: ActionEvent) => { + this.transitionWithEvent(new DragCanvasState(), event); + } + }) + ); + this.registerAction( new Action({ type: InputType.MOUSE_UP, diff --git a/packages/react-canvas-core/src/core-actions/Action.ts b/packages/react-canvas-core/src/core-actions/Action.ts index 931121f18..3a95d40ae 100644 --- a/packages/react-canvas-core/src/core-actions/Action.ts +++ b/packages/react-canvas-core/src/core-actions/Action.ts @@ -1,4 +1,4 @@ -import { MouseEvent, KeyboardEvent, WheelEvent, SyntheticEvent } from 'react'; +import { MouseEvent, KeyboardEvent, WheelEvent, TouchEvent, SyntheticEvent } from 'react'; import { Toolkit } from '../Toolkit'; import { CanvasEngine } from '../CanvasEngine'; import { BaseModel } from '../core-models/BaseModel'; @@ -9,7 +9,10 @@ export enum InputType { MOUSE_MOVE = 'mouse-move', MOUSE_WHEEL = 'mouse-wheel', KEY_DOWN = 'key-down', - KEY_UP = 'key-up' + KEY_UP = 'key-up', + TOUCH_START = 'touch-start', + TOUCH_END = 'touch-end', + TOUCH_MOVE = 'touch-move' } export interface Mapping { @@ -19,6 +22,9 @@ export interface Mapping { [InputType.MOUSE_WHEEL]: WheelEvent; [InputType.KEY_DOWN]: KeyboardEvent; [InputType.KEY_UP]: KeyboardEvent; + [InputType.TOUCH_START]: TouchEvent; + [InputType.TOUCH_END]: TouchEvent; + [InputType.TOUCH_MOVE]: TouchEvent; } export interface ActionEvent { diff --git a/packages/react-canvas-core/src/core-actions/ActionEventBus.ts b/packages/react-canvas-core/src/core-actions/ActionEventBus.ts index d62e9481b..769ca397c 100644 --- a/packages/react-canvas-core/src/core-actions/ActionEventBus.ts +++ b/packages/react-canvas-core/src/core-actions/ActionEventBus.ts @@ -63,7 +63,14 @@ export class ActionEventBus { return this.getActionsForType(InputType.MOUSE_MOVE); } else if (event.type === 'wheel') { return this.getActionsForType(InputType.MOUSE_WHEEL); + } else if (event.type === 'touchstart') { + return this.getActionsForType(InputType.TOUCH_START); + } else if (event.type === 'touchend') { + return this.getActionsForType(InputType.TOUCH_END); + } else if (event.type === 'touchmove') { + return this.getActionsForType(InputType.TOUCH_MOVE); } + return []; } diff --git a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts index d8548af0b..ff2e86191 100644 --- a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts +++ b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts @@ -7,7 +7,7 @@ export interface AbstractDisplacementStateEvent { displacementY: number; virtualDisplacementX: number; virtualDisplacementY: number; - event: React.MouseEvent; + event: React.MouseEvent | React.TouchEvent; } export abstract class AbstractDisplacementState extends State { @@ -22,11 +22,8 @@ export abstract class AbstractDisplacementState) => { - this.initialX = actionEvent.event.clientX; - this.initialY = actionEvent.event.clientY; - const rel = this.engine.getRelativePoint(actionEvent.event.clientX, actionEvent.event.clientY); - this.initialXRelative = rel.x; - this.initialYRelative = rel.y; + const { clientX, clientY } = actionEvent.event; + this.handleMoveStart(clientX, clientY); } }) ); @@ -44,25 +41,65 @@ export abstract class AbstractDisplacementState) => { - // when the mouse if up, we eject this state - this.eject(); + fire: () => this.handleMoveEnd() + }) + ); + + this.registerAction( + new Action({ + type: InputType.TOUCH_START, + fire: (actionEvent: ActionEvent) => { + const { clientX, clientY } = actionEvent.event.touches[0]; + this.handleMoveStart(clientX, clientY); + } + }) + ); + this.registerAction( + new Action({ + type: InputType.TOUCH_MOVE, + fire: (actionEvent: ActionEvent) => { + const { event } = actionEvent; + const { clientX, clientY } = event.touches[0]; + this.handleMove(clientX, clientY, event); } }) ); + this.registerAction( + new Action({ + type: InputType.TOUCH_END, + fire: () => this.handleMoveEnd() + }) + ); + } + + protected handleMoveStart(x: number, y: number): void { + this.initialX = x; + this.initialY = y; + const rel = this.engine.getRelativePoint(x, y); + this.initialXRelative = rel.x; + this.initialYRelative = rel.y; + } + + protected handleMove(x: number, y: number, event: React.MouseEvent | React.TouchEvent): void { + this.fireMouseMoved({ + displacementX: x - this.initialX, + displacementY: y - this.initialY, + virtualDisplacementX: (x - this.initialX) / (this.engine.getModel().getZoomLevel() / 100.0), + virtualDisplacementY: (y - this.initialY) / (this.engine.getModel().getZoomLevel() / 100.0), + event + }); + } + + protected handleMoveEnd(): void { + this.eject(); } abstract fireMouseMoved(event: AbstractDisplacementStateEvent); diff --git a/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx b/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx index 38e1546c0..77a06cf82 100644 --- a/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx +++ b/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx @@ -90,6 +90,15 @@ export class CanvasWidget extends React.Component { }} onMouseMove={(event) => { this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchStart={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchEnd={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchMove={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); }}> {model.getLayers().map((layer) => { return ( diff --git a/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx b/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx index d7642e580..573e0030d 100644 --- a/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx +++ b/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx @@ -16,6 +16,9 @@ namespace S { export class SelectionBoxWidget extends React.Component { render() { const { rect } = this.props; + + if (!rect) return null; + return ( ) => { + this.transitionWithEvent(new DragCanvasState(), event); + } + }) + ); } } diff --git a/packages/react-canvas-core/src/states/SelectionBoxState.ts b/packages/react-canvas-core/src/states/SelectionBoxState.ts index 648433418..5fa86cb22 100644 --- a/packages/react-canvas-core/src/states/SelectionBoxState.ts +++ b/packages/react-canvas-core/src/states/SelectionBoxState.ts @@ -1,7 +1,8 @@ +import { MouseEvent, TouchEvent } from 'react'; import { AbstractDisplacementState, AbstractDisplacementStateEvent } from '../core-state/AbstractDisplacementState'; import { State } from '../core-state/State'; import { SelectionLayerModel } from '../entities/selection/SelectionLayerModel'; -import { Rectangle } from '@projectstorm/geometry'; +import { Point, Rectangle } from '@projectstorm/geometry'; import { ModelGeometryInterface } from '../core/ModelGeometryInterface'; export class SelectionBoxState extends AbstractDisplacementState { @@ -26,7 +27,13 @@ export class SelectionBoxState extends AbstractDisplacementState { } getBoxDimensions(event: AbstractDisplacementStateEvent): ClientRect { - const rel = this.engine.getRelativePoint(event.event.clientX, event.event.clientY); + let rel: Point; + if (event.event instanceof MouseEvent) { + rel = this.engine.getRelativePoint(event.event.clientX, event.event.clientY); + } else if (event.event instanceof TouchEvent) { + const touch = event.event.touches[0]; + rel = this.engine.getRelativePoint(touch.clientX, touch.clientY); + } return { left: rel.x > this.initialXRelative ? this.initialXRelative : rel.x, diff --git a/packages/react-diagrams-core/src/states/DefaultDiagramState.ts b/packages/react-diagrams-core/src/states/DefaultDiagramState.ts index adadd2f37..d052e8df9 100644 --- a/packages/react-diagrams-core/src/states/DefaultDiagramState.ts +++ b/packages/react-diagrams-core/src/states/DefaultDiagramState.ts @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, TouchEvent } from 'react'; import { SelectingState, State, @@ -48,5 +48,15 @@ export class DefaultDiagramState extends State { } }) ); + + // touch drags the canvas + this.registerAction( + new Action({ + type: InputType.TOUCH_START, + fire: (event: ActionEvent) => { + this.transitionWithEvent(this.dragCanvas, event); + } + }) + ); } }