mirror of https://github.com/logseq/logseq
feat: move tool
parent
b12fd69be0
commit
dfd013f108
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './TLMoveTool'
|
|
@ -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' })
|
||||
}
|
||||
}
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from './PanningState'
|
||||
export * from './IdleState'
|
||||
export * from './IdleHoldState'
|
|
@ -5,3 +5,4 @@ export * from './TLEraseTool'
|
|||
export * from './TLLineTool'
|
||||
export * from './TLTextTool'
|
||||
export * from './TLSelectTool'
|
||||
export * from './TLMoveTool'
|
||||
|
|
Loading…
Reference in New Issue