[fix] arrows bind to locked shapes (#1833)

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 <mitja.bezensek@gmail.com>
This commit is contained in:
Steve Ruiz 2023-08-31 10:48:39 +02:00 committed by GitHub
parent 3bde22a482
commit a3a780414a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 2 deletions

View file

@ -239,10 +239,12 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
const point = this.editor.getShapePageTransform(shape.id)!.applyToPoint(handle) const point = this.editor.getShapePageTransform(shape.id)!.applyToPoint(handle)
const target = this.editor.getShapeAtPoint(point, { const target = this.editor.getShapeAtPoint(point, {
filter: (shape) => this.editor.getShapeUtil(shape).canBind(shape),
hitInside: true, hitInside: true,
hitFrameInside: true, hitFrameInside: true,
margin: 0, margin: 0,
filter: (targetShape) => {
return !targetShape.isLocked && this.editor.getShapeUtil(targetShape).canBind(targetShape)
},
}) })
if (!target) { if (!target) {

View file

@ -11,7 +11,9 @@ export class Pointing extends StateNode {
this.didTimeout = false this.didTimeout = false
const target = this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, { 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, margin: 0,
hitInside: true, hitInside: true,
}) })

View file

@ -169,6 +169,14 @@ describe('When binding an arrow to a shape', () => {
expect(arrow().props.end.type).toBe('point') 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', () => { it('should use timer on keyup when using control key to skip binding', () => {
editor.setCurrentTool('arrow') editor.setCurrentTool('arrow')
editor.pointerDown(0, 50) editor.pointerDown(0, 50)
@ -217,6 +225,20 @@ describe('When shapes are overlapping', () => {
expect(arrow().props.end).toMatchObject({ boundShapeId: ids.box4 }) 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', () => { it('binds to the highest shape or to the first filled shape', () => {
editor.updateShapes([ editor.updateShapes([
{ id: ids.box1, type: 'geo', props: { fill: 'solid' } }, { id: ids.box1, type: 'geo', props: { fill: 'solid' } },
@ -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', () => { it('starts a filled shape if it is above the hollow shape', () => {
// box2 - small, hollow // box2 - small, hollow
// box1 - big, filled // box1 - big, filled