arrows: update cursor only when in Select mode (#2742)

The cursor was updating for the arrow label even when in other tools
(e.g. Draw) and it should only be updating when in Select mode. That
addresses this issue: https://github.com/tldraw/tldraw/issues/2750

Also, there was a problem with arrows and zero length arrows and labels.
The problem was actually in `Vec.ts` where we were dividing by zero.
Addresses this bug https://github.com/tldraw/tldraw/issues/2749

### Change Type

- [x] `patch` — Bug fix

### Test Plan

1. Make sure that the cursor is only applicable to the Select tool.

### Release Notes

- Cursor tweak for arrow labels.
This commit is contained in:
Mime Čuvalo 2024-02-06 14:42:35 +00:00 committed by GitHub
parent 533d389953
commit 8cd8117acf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 48 additions and 27 deletions

View file

@ -1143,10 +1143,6 @@ input,
align-items: center; align-items: center;
} }
.tl-arrow-label__inner p {
cursor: var(--tl-cursor-grab);
}
.tl-arrow-label p, .tl-arrow-label p,
.tl-arrow-label textarea { .tl-arrow-label textarea {
margin: 0px; margin: 0px;

View file

@ -37,7 +37,9 @@ export function getCurvedArrowInfo(
const terminalsInArrowSpace = getArrowTerminalsInArrowSpace(editor, shape) const terminalsInArrowSpace = getArrowTerminalsInArrowSpace(editor, shape)
const med = Vec.Med(terminalsInArrowSpace.start, terminalsInArrowSpace.end) // point between start and end const med = Vec.Med(terminalsInArrowSpace.start, terminalsInArrowSpace.end) // point between start and end
const u = Vec.Sub(terminalsInArrowSpace.end, terminalsInArrowSpace.start).uni() // unit vector between start and end const distance = Vec.Sub(terminalsInArrowSpace.end, terminalsInArrowSpace.start)
// Check for divide-by-zero before we call uni()
const u = Vec.Len(distance) ? distance.uni() : Vec.From(distance) // unit vector between start and end
const middle = Vec.Add(med, u.per().mul(-bend)) // middle handle const middle = Vec.Add(med, u.per().mul(-bend)) // middle handle
const startShapeInfo = getBoundShapeInfoForTerminal(editor, shape.props.start) const startShapeInfo = getBoundShapeInfoForTerminal(editor, shape.props.start)

View file

@ -109,7 +109,9 @@ export function getStraightArrowInfo(editor: Editor, shape: TLArrowShape): TLArr
} }
} }
const u = Vec.Sub(b, a).uni() const distance = Vec.Sub(b, a)
// Check for divide-by-zero before we call uni()
const u = Vec.Len(distance) ? distance.uni() : Vec.From(distance)
const didFlip = !Vec.Equals(u, uAB) const didFlip = !Vec.Equals(u, uAB)
// If the arrow is bound non-exact to a start shape and the // If the arrow is bound non-exact to a start shape and the

View file

@ -143,6 +143,10 @@ describe('Vec.Uni', () => {
expect(Vec.Uni(new Vec(0, 10))).toMatchObject(new Vec(0, 1)) expect(Vec.Uni(new Vec(0, 10))).toMatchObject(new Vec(0, 1))
expect(Vec.Uni(new Vec(10, 10))).toMatchObject(new Vec(0.7071067811865475, 0.7071067811865475)) expect(Vec.Uni(new Vec(10, 10))).toMatchObject(new Vec(0.7071067811865475, 0.7071067811865475))
}) })
it('Divide-by-zero spits out NaN (at the moment)', () => {
expect(Vec.Uni(new Vec(0, 0))).toMatchObject(new Vec(NaN, NaN))
})
}) })
describe('Vec.Tan', () => { describe('Vec.Tan', () => {

View file

@ -33,6 +33,10 @@ function getArrowPoints(
: ints[0] : ints[0]
} }
if (isNaN(P0.x) || isNaN(P0.y)) {
P0 = info.start.point
}
return { return {
point: PT, point: PT,
int: P0, int: P0,

View file

@ -34,6 +34,13 @@ export class Idle extends StateNode {
override onPointerMove: TLEventHandlers['onPointerMove'] = () => { override onPointerMove: TLEventHandlers['onPointerMove'] = () => {
updateHoveredId(this.editor) updateHoveredId(this.editor)
const hitShape = this.editor.getHoveredShape()
if (this.isOverArrowLabelTest(hitShape)) {
this.editor.setCursor({ type: 'pointer', rotation: 0 })
} else {
this.editor.setCursor({ type: 'default', rotation: 0 })
}
} }
override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => { override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => {
@ -93,22 +100,11 @@ export class Idle extends StateNode {
} }
case 'shape': { case 'shape': {
const { shape } = info const { shape } = info
const pointInShapeSpace = this.editor.getPointInShapeSpace( if (this.isOverArrowLabelTest(shape)) {
shape,
this.editor.inputs.currentPagePoint
)
// todo: Extract into general hit test for arrows
if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
// How should we handle multiple labels? Do shapes ever have multiple labels?
const labelGeometry = this.editor.getShapeGeometry<Group2d>(shape).children[1]
// Knowing what we know about arrows... if the shape has no text in its label,
// then the label geometry should not be there.
if (labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)) {
// We're moving the label on a shape. // We're moving the label on a shape.
this.parent.transition('pointing_arrow_label', info) this.parent.transition('pointing_arrow_label', info)
break break
} }
}
if (this.editor.isShapeOrAncestorLocked(shape)) { if (this.editor.isShapeOrAncestorLocked(shape)) {
this.parent.transition('pointing_canvas', info) this.parent.transition('pointing_canvas', info)
@ -499,6 +495,28 @@ export class Idle extends StateNode {
isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1 isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1
isOverArrowLabelTest(shape: TLShape | undefined) {
if (!shape) return false
const pointInShapeSpace = this.editor.getPointInShapeSpace(
shape,
this.editor.inputs.currentPagePoint
)
// todo: Extract into general hit test for arrows
if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
// How should we handle multiple labels? Do shapes ever have multiple labels?
const labelGeometry = this.editor.getShapeGeometry<Group2d>(shape).children[1]
// Knowing what we know about arrows... if the shape has no text in its label,
// then the label geometry should not be there.
if (labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)) {
return true
}
}
return false
}
handleDoubleClickOnCanvas(info: TLClickEventInfo) { handleDoubleClickOnCanvas(info: TLClickEventInfo) {
// Create text shape and transition to editing_shape // Create text shape and transition to editing_shape
if (this.editor.getInstanceState().isReadonly) return if (this.editor.getInstanceState().isReadonly) return

View file

@ -24,12 +24,7 @@ export class PointingArrowLabel extends StateNode {
} }
private updateCursor() { private updateCursor() {
this.editor.updateInstanceState({ this.editor.setCursor({ type: 'grabbing', rotation: 0 })
cursor: {
type: 'grabbing',
rotation: 0,
},
})
} }
override onEnter = ( override onEnter = (