feat: move tool

pull/6385/head
Peng Xiao 2022-08-17 00:47:52 +08:00
parent b12fd69be0
commit dfd013f108
10 changed files with 132 additions and 7 deletions

View File

@ -1,4 +1,4 @@
import { TLSelectTool } from '@tldraw/core'
import { TLMoveTool, TLSelectTool } from '@tldraw/core'
import { useApp } from '@tldraw/react'
import { observer } from 'mobx-react-lite'
import * as React from 'react'
@ -22,7 +22,7 @@ const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonProps) =>
)
// Tool must exist
const Tool = app.Tools?.find(T => T.id === id) ?? TLSelectTool
const Tool = [...app.Tools, TLSelectTool, TLMoveTool]?.find(T => T.id === id)
const shortcut = ((Tool as any)['shortcut'] as string[])?.[0]
@ -97,6 +97,7 @@ export const PrimaryTools = observer(function PrimaryTools() {
<div className="tl-primary-tools">
<div className="tl-tools-floating-panel" data-tool-locked={app.settings.isToolLocked}>
<ToolButton title="Select" id="select" icon="select-cursor" />
<ToolButton title="Move" id="move" icon={app.isIn('move.panning') ? "hand-grab" : "hand-stop" } />
<ToolButton title="Draw" id="pencil" icon="ballpen" />
<ToolButton title="Highlight" id="highlighter" icon="highlight" />
<ToolButton title="Eraser" id="erase" icon="eraser" />

View File

@ -4,7 +4,7 @@ import { HighlighterShape, Shape } from '~lib'
export class HighlighterTool extends TLDrawTool<HighlighterShape, Shape, TLReactEventMap> {
static id = 'highlighter'
static shortcut = ['h']
static shortcut = ['m']
Shape = HighlighterShape
simplify = true
simplifyTolerance = 0.618

View File

@ -7,13 +7,15 @@ import { GRID_SIZE } from '~constants'
import {
TLInputs,
TLMoveTool,
TLPage,
TLPageModel,
TLSelectTool,
TLShape,
TLShapeConstructor,
TLShapeModel, TLToolConstructor,
TLViewport
TLShapeModel,
TLToolConstructor,
TLViewport,
} from '~lib'
import { TLApi } from '~lib/TLApi'
import { TLCursors } from '~lib/TLCursors'
@ -27,7 +29,7 @@ import type {
TLStateEvents,
TLSubscription,
TLSubscriptionEventInfo,
TLSubscriptionEventName
TLSubscriptionEventName,
} from '~types'
import { BoundsUtils, KeyUtils } from '~utils'
import { TLHistory } from '../TLHistory'
@ -51,7 +53,7 @@ export class TLApp<
Tools?: TLToolConstructor<S, K>[]
) {
super()
this._states = [TLSelectTool]
this._states = [TLSelectTool, TLMoveTool]
this.history.pause()
if (this.states && this.states.length > 0) {
this.registerStates(this.states)
@ -840,10 +842,24 @@ export class TLApp<
}
readonly onKeyDown: TLEvents<S, K>['keyboard'] = (info, e) => {
if (!this.editingShape && e['key'] === ' ' && !this.isIn('move')) {
e.stopPropagation()
e.preventDefault()
const prevTool = this.selectedTool
this.transition('move', { prevTool })
this.selectedTool.transition('idleHold')
return
}
this.inputs.onKeyDown(e)
}
readonly onKeyUp: TLEvents<S, K>['keyboard'] = (info, e) => {
if (!this.editingShape && e['key'] === ' ' && this.isIn('move')) {
this.selectedTool.transition('idle', { exit: true })
e.stopPropagation()
e.preventDefault()
return
}
this.inputs.onKeyUp(e)
}

View File

@ -0,0 +1,24 @@
import { TLApp, TLShape, TLTool } from '~lib'
import { TLCursor, TLEventMap } from '~types'
import { IdleHoldState, IdleState, PanningState } from './states'
export class TLMoveTool<
S extends TLShape = TLShape,
K extends TLEventMap = TLEventMap,
R extends TLApp<S, K> = TLApp<S, K>
> extends TLTool<S, K, R> {
static id = 'move'
static shortcut = ['h']
static states = [IdleState, IdleHoldState, PanningState]
static initial = 'idle'
cursor = TLCursor.Grab
prevTool: any = null
onEnter = (info: any) => {
this.prevTool = info?.prevTool
}
}

View File

@ -0,0 +1 @@
export * from './TLMoveTool'

View File

@ -0,0 +1,17 @@
import { TLApp, TLShape, TLToolState } from '~lib'
import type { TLEventMap, TLStateEvents } from '~types'
import type { TLMoveTool } from '../TLMoveTool'
export class IdleHoldState<
S extends TLShape,
K extends TLEventMap,
R extends TLApp<S, K>,
P extends TLMoveTool<S, K, R>
> extends TLToolState<S, K, R, P> {
static id = 'idleHold'
onPointerDown: TLStateEvents<S, K>['onPointerDown'] = (info, e) => {
if (info.order) return
this.tool.transition('panning', { prevState: 'idleHold' })
}
}

View File

@ -0,0 +1,27 @@
import { TLApp, TLShape, TLToolState } from '~lib'
import type { TLEventMap, TLStateEvents } from '~types'
import type { TLMoveTool } from '../TLMoveTool'
export class IdleState<
S extends TLShape,
K extends TLEventMap,
R extends TLApp<S, K>,
P extends TLMoveTool<S, K, R>
> extends TLToolState<S, K, R, P> {
static id = 'idle'
onEnter = (info: any) => {
if (this.parent.prevTool && info.exit) {
this.app.setCurrentState(this.parent.prevTool)
setTimeout(() => {
this.app.cursors.reset()
this.app.cursors.setCursor(this.parent.prevTool.cursor)
})
}
}
onPointerDown: TLStateEvents<S, K>['onPointerDown'] = (info, e) => {
if (info.order) return
this.tool.transition('panning')
}
}

View File

@ -0,0 +1,35 @@
import Vec from '@tldraw/vec'
import { TLApp, TLShape, TLToolState } from '~lib'
import { TLCursor, TLEventMap, TLStateEvents } from '~types'
import type { TLMoveTool } from '../TLMoveTool'
export class PanningState<
S extends TLShape,
K extends TLEventMap,
R extends TLApp<S, K>,
P extends TLMoveTool<S, K, R>
> extends TLToolState<S, K, R, P> {
static id = 'panning'
cursor = TLCursor.Grabbing
originalScreenPoint: number[] = []
originalCameraPoint: number[] = []
prevState = 'idle'
onEnter = (info: any) => {
this.prevState = info?.prevState
this.originalScreenPoint = this.app.inputs.currentScreenPoint
this.originalCameraPoint = this.app.viewport.camera.point
}
onPointerMove: TLStateEvents<S, K>['onPointerMove'] = (_, e: K['pointer']) => {
const delta = Vec.sub(this.originalScreenPoint, this.app.inputs.currentScreenPoint)
this.app.viewport.update({
point: Vec.sub(this.originalCameraPoint, Vec.div(delta, this.app.viewport.camera.zoom)),
})
}
onPointerUp: TLStateEvents<S, K>['onPointerUp'] = () => {
this.tool.transition(this.prevState ?? 'idle')
}
}

View File

@ -0,0 +1,3 @@
export * from './PanningState'
export * from './IdleState'
export * from './IdleHoldState'

View File

@ -5,3 +5,4 @@ export * from './TLEraseTool'
export * from './TLLineTool'
export * from './TLTextTool'
export * from './TLSelectTool'
export * from './TLMoveTool'