diff --git a/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts b/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts index fe88ef9..0d569e8 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 931121f..3a95d40 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 d62e948..769ca39 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 d8548af..5a4b332 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 { @@ -63,6 +63,45 @@ export abstract class AbstractDisplacementState) => { + const touch = actionEvent.event.touches[0]; + this.initialX = touch.clientX; + this.initialY = touch.clientY; + const rel = this.engine.getRelativePoint(touch.clientX, touch.clientY); + this.initialXRelative = rel.x; + this.initialYRelative = rel.y; + } + }) + ); + this.registerAction( + new Action({ + type: InputType.TOUCH_MOVE, + fire: (actionEvent: ActionEvent) => { + const { event } = actionEvent; + const touch = event.touches[0]; + this.fireMouseMoved({ + displacementX: touch.clientX - this.initialX, + displacementY: touch.clientY - this.initialY, + virtualDisplacementX: (touch.clientX - this.initialX) / (this.engine.getModel().getZoomLevel() / 100.0), + virtualDisplacementY: (touch.clientY - this.initialY) / (this.engine.getModel().getZoomLevel() / 100.0), + event: event + }); + } + }) + ); + this.registerAction( + new Action({ + type: InputType.TOUCH_END, + fire: (event: ActionEvent) => { + // when the mouse if up, we eject this state + 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 38e1546..77a06cf 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 d7642e5..314dde4 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; + 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 6484334..5fa86cb 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 adadd2f..d052e8d 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); + } + }) + ); } }