kopia lustrzana https://github.com/Tldraw/Tldraw
155 wiersze
3.8 KiB
TypeScript
155 wiersze
3.8 KiB
TypeScript
import {
|
|
Mat,
|
|
StateNode,
|
|
TLEventHandlers,
|
|
TLInterruptEvent,
|
|
TLLineShape,
|
|
TLShapeId,
|
|
Vec,
|
|
createShapeId,
|
|
getIndexAbove,
|
|
last,
|
|
sortByIndex,
|
|
structuredClone,
|
|
} from '@tldraw/editor'
|
|
|
|
const MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES = 2
|
|
|
|
export class Pointing extends StateNode {
|
|
static override id = 'pointing'
|
|
|
|
shape = {} as TLLineShape
|
|
|
|
markId: string | undefined
|
|
|
|
override onEnter = (info: { shapeId?: TLShapeId }) => {
|
|
const { inputs } = this.editor
|
|
const { currentPagePoint } = inputs
|
|
|
|
this.markId = undefined
|
|
|
|
// Previously created line shape that we might be extending
|
|
const shape = info.shapeId && this.editor.getShape<TLLineShape>(info.shapeId)
|
|
|
|
if (shape && inputs.shiftKey) {
|
|
// Extending a previous shape
|
|
this.markId = `creating:${shape.id}`
|
|
this.editor.mark(this.markId)
|
|
this.shape = shape
|
|
|
|
const handles = this.editor.getShapeHandles(this.shape)
|
|
if (!handles) return
|
|
|
|
const vertexHandles = handles.filter((h) => h.type === 'vertex').sort(sortByIndex)
|
|
const endHandle = vertexHandles[vertexHandles.length - 1]
|
|
const prevEndHandle = vertexHandles[vertexHandles.length - 2]
|
|
|
|
const shapePagePoint = Mat.applyToPoint(
|
|
this.editor.getShapeParentTransform(this.shape)!,
|
|
new Vec(this.shape.x, this.shape.y)
|
|
)
|
|
|
|
const nextPoint = Vec.Sub(currentPagePoint, shapePagePoint).addXY(0.1, 0.1)
|
|
const points = structuredClone(this.shape.props.points)
|
|
|
|
if (
|
|
Vec.DistMin(endHandle, prevEndHandle, MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES) ||
|
|
Vec.DistMin(nextPoint, endHandle, MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES)
|
|
) {
|
|
// Don't add a new point if the distance between the last two points is too small
|
|
points[endHandle.id] = {
|
|
id: endHandle.id,
|
|
index: endHandle.index,
|
|
x: nextPoint.x,
|
|
y: nextPoint.y,
|
|
}
|
|
} else {
|
|
// Add a new point
|
|
const nextIndex = getIndexAbove(endHandle.index)
|
|
points[nextIndex] = {
|
|
id: nextIndex,
|
|
index: nextIndex,
|
|
x: nextPoint.x,
|
|
y: nextPoint.y,
|
|
}
|
|
}
|
|
|
|
this.editor.updateShapes([
|
|
{
|
|
id: this.shape.id,
|
|
type: this.shape.type,
|
|
props: {
|
|
points,
|
|
},
|
|
},
|
|
])
|
|
} else {
|
|
const id = createShapeId()
|
|
|
|
this.markId = `creating:${id}`
|
|
this.editor.mark(this.markId)
|
|
|
|
this.editor.createShapes<TLLineShape>([
|
|
{
|
|
id,
|
|
type: 'line',
|
|
x: currentPagePoint.x,
|
|
y: currentPagePoint.y,
|
|
},
|
|
])
|
|
|
|
this.editor.select(id)
|
|
this.shape = this.editor.getShape(id)!
|
|
}
|
|
}
|
|
|
|
override onPointerMove: TLEventHandlers['onPointerMove'] = () => {
|
|
if (!this.shape) return
|
|
|
|
if (this.editor.inputs.isDragging) {
|
|
const handles = this.editor.getShapeHandles(this.shape)
|
|
if (!handles) {
|
|
if (this.markId) this.editor.bailToMark(this.markId)
|
|
throw Error('No handles found')
|
|
}
|
|
const lastHandle = last(handles)!
|
|
this.editor.setCurrentTool('select.dragging_handle', {
|
|
shape: this.shape,
|
|
isCreating: true,
|
|
// remove the offset that we added to the handle when we created it
|
|
handle: { ...lastHandle, x: lastHandle.x - 0.1, y: lastHandle.y - 0.1 },
|
|
onInteractionEnd: 'line',
|
|
})
|
|
}
|
|
}
|
|
|
|
override onPointerUp: TLEventHandlers['onPointerUp'] = () => {
|
|
this.complete()
|
|
}
|
|
|
|
override onCancel: TLEventHandlers['onCancel'] = () => {
|
|
this.cancel()
|
|
}
|
|
|
|
override onComplete: TLEventHandlers['onComplete'] = () => {
|
|
this.complete()
|
|
}
|
|
|
|
override onInterrupt: TLInterruptEvent = () => {
|
|
this.parent.transition('idle')
|
|
if (this.markId) this.editor.bailToMark(this.markId)
|
|
this.editor.snaps.clearIndicators()
|
|
}
|
|
|
|
complete() {
|
|
this.parent.transition('idle', { shapeId: this.shape.id })
|
|
this.editor.snaps.clearIndicators()
|
|
}
|
|
|
|
cancel() {
|
|
if (this.markId) this.editor.bailToMark(this.markId)
|
|
this.parent.transition('idle', { shapeId: this.shape.id })
|
|
this.editor.snaps.clearIndicators()
|
|
}
|
|
}
|