Skip to content

Commit

Permalink
[fix] arrows bind to locked shapes (tldraw#1833)
Browse files Browse the repository at this point in the history
This PR fixes a bug where arrows would bind to locked shapes.

### Change Type

- [x] `patch` — Bug fix

### Test Plan

1. Lock a shape.
2. Confirm that an arrow can neither begin bound to the shape
3. Confirm that an arrow cannot bind to the shape

- [ ] Unit Tests
- [ ] End to end tests

---------

Co-authored-by: Mitja Bezenšek <[email protected]>
  • Loading branch information
steveruizok and MitjaBezensek authored Aug 31, 2023
1 parent 3bde22a commit a3a7804
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
4 changes: 3 additions & 1 deletion packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,12 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
const point = this.editor.getShapePageTransform(shape.id)!.applyToPoint(handle)

const target = this.editor.getShapeAtPoint(point, {
filter: (shape) => this.editor.getShapeUtil(shape).canBind(shape),
hitInside: true,
hitFrameInside: true,
margin: 0,
filter: (targetShape) => {
return !targetShape.isLocked && this.editor.getShapeUtil(targetShape).canBind(targetShape)
},
})

if (!target) {
Expand Down
4 changes: 3 additions & 1 deletion packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export class Pointing extends StateNode {
this.didTimeout = false

const target = this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
filter: (shape) => this.editor.getShapeUtil(shape).canBind(shape),
filter: (targetShape) => {
return !targetShape.isLocked && this.editor.getShapeUtil(targetShape).canBind(targetShape)
},
margin: 0,
hitInside: true,
})
Expand Down
45 changes: 45 additions & 0 deletions packages/tldraw/src/test/arrows-megabus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ describe('When binding an arrow to a shape', () => {
expect(arrow().props.end.type).toBe('point')
})

it('does not bind when the shape is locked', () => {
editor.toggleLock(editor.currentPageShapes)
editor.setCurrentTool('arrow')
editor.pointerDown(0, 50)
editor.pointerMove(100, 50)
expect(arrow().props.end.type).toBe('point')
})

it('should use timer on keyup when using control key to skip binding', () => {
editor.setCurrentTool('arrow')
editor.pointerDown(0, 50)
Expand Down Expand Up @@ -217,6 +225,20 @@ describe('When shapes are overlapping', () => {
expect(arrow().props.end).toMatchObject({ boundShapeId: ids.box4 })
})

it('does not bind when shapes are locked', () => {
editor.toggleLock([ids.box1, ids.box2, ids.box4])
editor.setCurrentTool('arrow')
editor.pointerDown(0, 50)
editor.pointerMove(125, 50) // over box1 only
expect(arrow().props.end).toMatchObject({ type: 'point' }) // box 1 is locked!
editor.pointerMove(175, 50) // box2 is higher
expect(arrow().props.end).toMatchObject({ type: 'point' }) // box 2 is locked! box1 is locked!
editor.pointerMove(225, 50) // box3 is higher
expect(arrow().props.end).toMatchObject({ boundShapeId: ids.box3 })
editor.pointerMove(275, 50) // box4 is higher
expect(arrow().props.end).toMatchObject({ boundShapeId: ids.box3 }) // box 4 is locked!
})

it('binds to the highest shape or to the first filled shape', () => {
editor.updateShapes([
{ id: ids.box1, type: 'geo', props: { fill: 'solid' } },
Expand Down Expand Up @@ -445,6 +467,29 @@ describe('When starting an arrow inside of multiple shapes', () => {
})
})

it('skips locked shape when starting an arrow over shapes', () => {
editor.toggleLock([ids.box2])
editor.sendToBack([ids.box2])
// box1 is bigger and is above box2

editor.setCurrentTool('arrow')
editor.pointerDown(25, 25)
expect(editor.currentPageShapes.length).toBe(2)
expect(arrow()).toBe(null)
editor.pointerMove(30, 30)
expect(editor.currentPageShapes.length).toBe(3)
expect(arrow()).toMatchObject({
props: {
start: {
boundShapeId: ids.box1, // not box 2!
},
end: {
boundShapeId: ids.box1, // not box 2
},
},
})
})

it('starts a filled shape if it is above the hollow shape', () => {
// box2 - small, hollow
// box1 - big, filled
Expand Down

0 comments on commit a3a7804

Please sign in to comment.