tldraw 4.1.0-canary.d716f21afebb → 4.1.0-canary.d89f813fd441

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist-cjs/index.d.ts +11 -2
  2. package/dist-cjs/index.js +4 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +20 -4
  5. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  6. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +23 -11
  7. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
  8. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +18 -5
  9. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  10. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +21 -9
  11. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  12. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +24 -8
  13. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
  14. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +21 -9
  15. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
  16. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +23 -8
  17. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +21 -9
  19. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  20. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +26 -11
  21. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  22. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +2 -2
  23. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +1 -1
  24. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +63 -55
  25. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  26. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +54 -47
  27. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +3 -3
  28. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js +62 -55
  29. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +2 -2
  30. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js +12 -5
  31. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +2 -2
  32. package/dist-cjs/lib/ui/components/menu-items.js +2 -2
  33. package/dist-cjs/lib/ui/components/menu-items.js.map +1 -1
  34. package/dist-cjs/lib/ui/hooks/useEditorEvents.js +1 -1
  35. package/dist-cjs/lib/ui/hooks/useEditorEvents.js.map +1 -1
  36. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +4 -4
  37. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +1 -1
  38. package/dist-cjs/lib/ui/version.js +3 -3
  39. package/dist-cjs/lib/ui/version.js.map +1 -1
  40. package/dist-esm/index.d.mts +11 -2
  41. package/dist-esm/index.mjs +10 -4
  42. package/dist-esm/index.mjs.map +2 -2
  43. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +20 -4
  44. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  45. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +23 -11
  46. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
  47. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +18 -5
  48. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  49. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +21 -9
  50. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  51. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +24 -8
  52. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
  53. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +21 -9
  54. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
  55. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +23 -8
  56. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  57. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +21 -9
  58. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  59. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +26 -11
  60. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  61. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +2 -2
  62. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +1 -1
  63. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +68 -57
  64. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  65. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +54 -47
  66. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +3 -3
  67. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs +63 -56
  68. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +2 -2
  69. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs +12 -5
  70. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +2 -2
  71. package/dist-esm/lib/ui/components/menu-items.mjs +2 -2
  72. package/dist-esm/lib/ui/components/menu-items.mjs.map +1 -1
  73. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs +1 -1
  74. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs.map +1 -1
  75. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +4 -4
  76. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +1 -1
  77. package/dist-esm/lib/ui/version.mjs +3 -3
  78. package/dist-esm/lib/ui/version.mjs.map +1 -1
  79. package/package.json +3 -3
  80. package/src/index.ts +3 -0
  81. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +23 -6
  82. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +24 -12
  83. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +21 -10
  84. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +23 -11
  85. package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +26 -9
  86. package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +23 -10
  87. package/src/lib/tools/SelectTool/childStates/Resizing.ts +24 -9
  88. package/src/lib/tools/SelectTool/childStates/Rotating.ts +27 -11
  89. package/src/lib/tools/SelectTool/childStates/Translating.ts +28 -12
  90. package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +2 -2
  91. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +60 -49
  92. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +70 -53
  93. package/src/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.tsx +105 -90
  94. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +72 -51
  95. package/src/lib/ui/components/menu-items.tsx +2 -2
  96. package/src/lib/ui/hooks/useEditorEvents.ts +1 -1
  97. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +4 -4
  98. package/src/lib/ui/version.ts +3 -3
  99. package/src/test/SelectTool.test.ts +251 -0
package/src/index.ts CHANGED
@@ -465,6 +465,7 @@ export {
465
465
  } from './lib/ui/components/StylePanel/DefaultStylePanelContent'
466
466
  export {
467
467
  StylePanelButtonPicker,
468
+ StylePanelButtonPickerInline,
468
469
  type StylePanelButtonPickerProps,
469
470
  } from './lib/ui/components/StylePanel/StylePanelButtonPicker'
470
471
  export {
@@ -475,10 +476,12 @@ export {
475
476
  } from './lib/ui/components/StylePanel/StylePanelContext'
476
477
  export {
477
478
  StylePanelDoubleDropdownPicker,
479
+ StylePanelDoubleDropdownPickerInline,
478
480
  type StylePanelDoubleDropdownPickerProps,
479
481
  } from './lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker'
480
482
  export {
481
483
  StylePanelDropdownPicker,
484
+ StylePanelDropdownPickerInline,
482
485
  type StylePanelDropdownPickerProps,
483
486
  } from './lib/ui/components/StylePanel/StylePanelDropdownPicker'
484
487
  export {
@@ -17,7 +17,7 @@ export class Cropping extends StateNode {
17
17
  info = {} as TLPointerEventInfo & {
18
18
  target: 'selection'
19
19
  handle: SelectionHandle
20
- onInteractionEnd?: string
20
+ onInteractionEnd?: string | (() => void)
21
21
  }
22
22
 
23
23
  markId = ''
@@ -28,10 +28,13 @@ export class Cropping extends StateNode {
28
28
  info: TLPointerEventInfo & {
29
29
  target: 'selection'
30
30
  handle: SelectionHandle
31
- onInteractionEnd?: string
31
+ onInteractionEnd?: string | (() => void)
32
32
  }
33
33
  ) {
34
34
  this.info = info
35
+ if (typeof info.onInteractionEnd === 'string') {
36
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
37
+ }
35
38
  this.markId = this.editor.markHistoryStoppingPoint('cropping')
36
39
  this.snapshot = this.createSnapshot()
37
40
  this.updateShapes()
@@ -61,6 +64,10 @@ export class Cropping extends StateNode {
61
64
  this.cancel()
62
65
  }
63
66
 
67
+ override onExit() {
68
+ this.parent.setCurrentToolIdMask(undefined)
69
+ }
70
+
64
71
  private updateCursor() {
65
72
  const selectedShape = this.editor.getSelectedShapes()[0]
66
73
  if (!selectedShape) return
@@ -108,8 +115,13 @@ export class Cropping extends StateNode {
108
115
  private complete() {
109
116
  this.updateShapes()
110
117
  kickoutOccludedShapes(this.editor, [this.snapshot.shape.id])
111
- if (this.info.onInteractionEnd) {
112
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
118
+ const { onInteractionEnd } = this.info
119
+ if (onInteractionEnd) {
120
+ if (typeof onInteractionEnd === 'string') {
121
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
122
+ } else {
123
+ onInteractionEnd()
124
+ }
113
125
  } else {
114
126
  this.editor.setCroppingShape(null)
115
127
  this.editor.setCurrentTool('select.idle')
@@ -118,8 +130,13 @@ export class Cropping extends StateNode {
118
130
 
119
131
  private cancel() {
120
132
  this.editor.bailToMark(this.markId)
121
- if (this.info.onInteractionEnd) {
122
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
133
+ const { onInteractionEnd } = this.info
134
+ if (onInteractionEnd) {
135
+ if (typeof onInteractionEnd === 'string') {
136
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
137
+ } else {
138
+ onInteractionEnd()
139
+ }
123
140
  } else {
124
141
  this.editor.setCroppingShape(null)
125
142
  this.editor.setCurrentTool('select.idle')
@@ -4,7 +4,7 @@ import { CursorTypeMap } from '../../PointingResizeHandle'
4
4
  type TLPointingCropHandleInfo = TLPointerEventInfo & {
5
5
  target: 'selection'
6
6
  } & {
7
- onInteractionEnd?: string
7
+ onInteractionEnd?: string | (() => void)
8
8
  }
9
9
 
10
10
  export class PointingCropHandle extends StateNode {
@@ -14,7 +14,9 @@ export class PointingCropHandle extends StateNode {
14
14
 
15
15
  override onEnter(info: TLPointingCropHandleInfo) {
16
16
  this.info = info
17
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
17
+ if (typeof info.onInteractionEnd === 'string') {
18
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
19
+ }
18
20
  const selectedShape = this.editor.getSelectedShapes()[0]
19
21
  if (!selectedShape) return
20
22
 
@@ -47,12 +49,17 @@ export class PointingCropHandle extends StateNode {
47
49
  }
48
50
 
49
51
  override onPointerUp() {
50
- if (this.info.onInteractionEnd) {
51
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
52
- } else {
53
- this.editor.setCroppingShape(null)
54
- this.editor.setCurrentTool('select.idle')
52
+ const { onInteractionEnd } = this.info
53
+ if (onInteractionEnd) {
54
+ if (typeof onInteractionEnd === 'string') {
55
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
56
+ } else {
57
+ onInteractionEnd()
58
+ }
59
+ return
55
60
  }
61
+ this.editor.setCroppingShape(null)
62
+ this.editor.setCurrentTool('select.idle')
56
63
  }
57
64
 
58
65
  override onCancel() {
@@ -68,11 +75,16 @@ export class PointingCropHandle extends StateNode {
68
75
  }
69
76
 
70
77
  private cancel() {
71
- if (this.info.onInteractionEnd) {
72
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
73
- } else {
74
- this.editor.setCroppingShape(null)
75
- this.editor.setCurrentTool('select.idle')
78
+ const { onInteractionEnd } = this.info
79
+ if (onInteractionEnd) {
80
+ if (typeof onInteractionEnd === 'string') {
81
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
82
+ } else {
83
+ onInteractionEnd()
84
+ }
85
+ return
76
86
  }
87
+ this.editor.setCroppingShape(null)
88
+ this.editor.setCurrentTool('select.idle')
77
89
  }
78
90
  }
@@ -21,7 +21,7 @@ import { getArrowBindings } from '../../../shapes/arrow/shared'
21
21
  export type DraggingHandleInfo = TLPointerEventInfo & {
22
22
  shape: TLArrowShape | TLLineShape
23
23
  target: 'handle'
24
- onInteractionEnd?: string
24
+ onInteractionEnd?: string | (() => void)
25
25
  isCreating?: boolean
26
26
  creatingMarkId?: string
27
27
  }
@@ -47,7 +47,9 @@ export class DraggingHandle extends StateNode {
47
47
  override onEnter(info: DraggingHandleInfo) {
48
48
  const { shape, isCreating, creatingMarkId, handle } = info
49
49
  this.info = info
50
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
50
+ if (typeof info.onInteractionEnd === 'string') {
51
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
52
+ }
51
53
  this.shapeId = shape.id
52
54
  this.markId = ''
53
55
 
@@ -220,11 +222,17 @@ export class DraggingHandle extends StateNode {
220
222
  }
221
223
 
222
224
  const { onInteractionEnd } = this.info
223
- if (this.editor.getInstanceState().isToolLocked && onInteractionEnd) {
224
- // Return to the tool that was active before this one,
225
- // but only if tool lock is turned on!
226
- this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })
227
- return
225
+ if (onInteractionEnd) {
226
+ if (typeof onInteractionEnd === 'string') {
227
+ if (this.editor.getInstanceState().isToolLocked && onInteractionEnd) {
228
+ // Return to the tool that was active before this one but only if tool lock is turned on!
229
+ this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })
230
+ return
231
+ }
232
+ } else {
233
+ onInteractionEnd?.()
234
+ return
235
+ }
228
236
  }
229
237
 
230
238
  this.parent.transition('idle')
@@ -249,9 +257,12 @@ export class DraggingHandle extends StateNode {
249
257
 
250
258
  const { onInteractionEnd } = this.info
251
259
  if (onInteractionEnd) {
252
- // Return to the tool that was active before this one,
253
- // whether tool lock is turned on or not!
254
- this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })
260
+ if (typeof onInteractionEnd === 'string') {
261
+ // Return to the tool that was active before this one, whether tool lock is turned on or not!
262
+ this.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })
263
+ } else {
264
+ onInteractionEnd?.()
265
+ }
255
266
  return
256
267
  }
257
268
 
@@ -23,7 +23,7 @@ export class PointingArrowLabel extends StateNode {
23
23
 
24
24
  private info = {} as TLPointerEventInfo & {
25
25
  shape: TLArrowShape
26
- onInteractionEnd?: string
26
+ onInteractionEnd?: string | (() => void)
27
27
  isCreating: boolean
28
28
  }
29
29
 
@@ -34,12 +34,14 @@ export class PointingArrowLabel extends StateNode {
34
34
  override onEnter(
35
35
  info: TLPointerEventInfo & {
36
36
  shape: TLArrowShape
37
- onInteractionEnd?: string
37
+ onInteractionEnd?: string | (() => void)
38
38
  isCreating: boolean
39
39
  }
40
40
  ) {
41
41
  const { shape } = info
42
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
42
+ if (typeof info.onInteractionEnd === 'string') {
43
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
44
+ }
43
45
  this.info = info
44
46
  this.shapeId = shape.id
45
47
  this.didDrag = false
@@ -155,20 +157,30 @@ export class PointingArrowLabel extends StateNode {
155
157
  }
156
158
 
157
159
  private complete() {
158
- if (this.info.onInteractionEnd) {
159
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
160
- } else {
161
- this.parent.transition('idle')
160
+ const { onInteractionEnd } = this.info
161
+ if (onInteractionEnd) {
162
+ if (typeof onInteractionEnd === 'string') {
163
+ this.editor.setCurrentTool(onInteractionEnd, {})
164
+ } else {
165
+ onInteractionEnd()
166
+ }
167
+ return
162
168
  }
169
+ this.parent.transition('idle')
163
170
  }
164
171
 
165
172
  private cancel() {
166
173
  this.editor.bailToMark(this.markId)
167
174
 
168
- if (this.info.onInteractionEnd) {
169
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
170
- } else {
171
- this.parent.transition('idle')
175
+ const { onInteractionEnd } = this.info
176
+ if (onInteractionEnd) {
177
+ if (typeof onInteractionEnd === 'string') {
178
+ this.editor.setCurrentTool(onInteractionEnd, {})
179
+ } else {
180
+ onInteractionEnd()
181
+ }
182
+ return
172
183
  }
184
+ this.parent.transition('idle')
173
185
  }
174
186
  }
@@ -17,7 +17,7 @@ export const CursorTypeMap: Record<TLSelectionHandle, TLCursorType> = {
17
17
  }
18
18
 
19
19
  type PointingResizeHandleInfo = Extract<TLPointerEventInfo, { target: 'selection' }> & {
20
- onInteractionEnd?: string
20
+ onInteractionEnd?: string | (() => void)
21
21
  }
22
22
 
23
23
  export class PointingResizeHandle extends StateNode {
@@ -36,9 +36,16 @@ export class PointingResizeHandle extends StateNode {
36
36
 
37
37
  override onEnter(info: PointingResizeHandleInfo) {
38
38
  this.info = info
39
+ if (typeof info.onInteractionEnd === 'string') {
40
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
41
+ }
39
42
  this.updateCursor()
40
43
  }
41
44
 
45
+ override onExit() {
46
+ this.parent.setCurrentToolIdMask(undefined)
47
+ }
48
+
42
49
  override onPointerMove() {
43
50
  if (this.editor.inputs.isDragging) {
44
51
  this.startResizing()
@@ -71,18 +78,28 @@ export class PointingResizeHandle extends StateNode {
71
78
  }
72
79
 
73
80
  private complete() {
74
- if (this.info.onInteractionEnd) {
75
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
76
- } else {
77
- this.parent.transition('idle')
81
+ const { onInteractionEnd } = this.info
82
+ if (onInteractionEnd) {
83
+ if (typeof onInteractionEnd === 'string') {
84
+ this.editor.setCurrentTool(onInteractionEnd, {})
85
+ } else {
86
+ onInteractionEnd()
87
+ }
88
+ return
78
89
  }
90
+ this.parent.transition('idle')
79
91
  }
80
92
 
81
93
  private cancel() {
82
- if (this.info.onInteractionEnd) {
83
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
84
- } else {
85
- this.parent.transition('idle')
94
+ const { onInteractionEnd } = this.info
95
+ if (onInteractionEnd) {
96
+ if (typeof onInteractionEnd === 'string') {
97
+ this.editor.setCurrentTool(onInteractionEnd, {})
98
+ } else {
99
+ onInteractionEnd()
100
+ }
101
+ return
86
102
  }
103
+ this.parent.transition('idle')
87
104
  }
88
105
  }
@@ -2,7 +2,7 @@ import { RotateCorner, StateNode, TLPointerEventInfo } from '@tldraw/editor'
2
2
  import { CursorTypeMap } from './PointingResizeHandle'
3
3
 
4
4
  type PointingRotateHandleInfo = Extract<TLPointerEventInfo, { target: 'selection' }> & {
5
- onInteractionEnd?: string
5
+ onInteractionEnd?: string | (() => void)
6
6
  }
7
7
 
8
8
  export class PointingRotateHandle extends StateNode {
@@ -18,8 +18,10 @@ export class PointingRotateHandle extends StateNode {
18
18
  }
19
19
 
20
20
  override onEnter(info: PointingRotateHandleInfo) {
21
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
22
21
  this.info = info
22
+ if (typeof info.onInteractionEnd === 'string') {
23
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
24
+ }
23
25
  this.updateCursor()
24
26
  }
25
27
 
@@ -60,18 +62,29 @@ export class PointingRotateHandle extends StateNode {
60
62
  }
61
63
 
62
64
  private complete() {
63
- if (this.info.onInteractionEnd) {
64
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
65
- } else {
66
- this.parent.transition('idle')
65
+ const { onInteractionEnd } = this.info
66
+ if (onInteractionEnd) {
67
+ if (typeof onInteractionEnd === 'string') {
68
+ // Return to the tool that was active before this one, whether tool lock is turned on or not!
69
+ this.editor.setCurrentTool(onInteractionEnd, {})
70
+ } else {
71
+ onInteractionEnd?.()
72
+ }
73
+ return
67
74
  }
75
+ this.parent.transition('idle')
68
76
  }
69
77
 
70
78
  private cancel() {
71
- if (this.info.onInteractionEnd) {
72
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
73
- } else {
74
- this.parent.transition('idle')
79
+ const { onInteractionEnd } = this.info
80
+ if (onInteractionEnd) {
81
+ if (typeof onInteractionEnd === 'string') {
82
+ this.editor.setCurrentTool(onInteractionEnd, {})
83
+ } else {
84
+ onInteractionEnd()
85
+ }
86
+ return
75
87
  }
88
+ this.parent.transition('idle')
76
89
  }
77
90
  }
@@ -29,7 +29,7 @@ export type ResizingInfo = TLPointerEventInfo & {
29
29
  creatingMarkId?: string
30
30
  onCreate?(shape: TLShape | null): void
31
31
  creationCursorOffset?: VecLike
32
- onInteractionEnd?: string
32
+ onInteractionEnd?: string | (() => void)
33
33
  }
34
34
 
35
35
  export class Resizing extends StateNode {
@@ -55,7 +55,9 @@ export class Resizing extends StateNode {
55
55
  this.info = info
56
56
  this.didHoldCommand = false
57
57
 
58
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
58
+ if (typeof info.onInteractionEnd === 'string') {
59
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
60
+ }
59
61
  this.creationCursorOffset = creationCursorOffset
60
62
 
61
63
  try {
@@ -135,11 +137,16 @@ export class Resizing extends StateNode {
135
137
 
136
138
  this.editor.bailToMark(this.markId)
137
139
 
138
- if (this.info.onInteractionEnd) {
139
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
140
- } else {
141
- this.parent.transition('idle')
140
+ const { onInteractionEnd } = this.info
141
+ if (onInteractionEnd) {
142
+ if (typeof onInteractionEnd === 'string') {
143
+ this.editor.setCurrentTool(onInteractionEnd, {})
144
+ } else {
145
+ onInteractionEnd()
146
+ }
147
+ return
142
148
  }
149
+ this.parent.transition('idle')
143
150
  }
144
151
 
145
152
  private complete() {
@@ -152,9 +159,17 @@ export class Resizing extends StateNode {
152
159
  return
153
160
  }
154
161
 
155
- if (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {
156
- this.editor.setCurrentTool(this.info.onInteractionEnd, {})
157
- return
162
+ const { onInteractionEnd } = this.info
163
+ if (onInteractionEnd) {
164
+ if (typeof onInteractionEnd === 'string') {
165
+ if (this.editor.getInstanceState().isToolLocked) {
166
+ this.editor.setCurrentTool(onInteractionEnd, {})
167
+ return
168
+ }
169
+ } else {
170
+ onInteractionEnd()
171
+ return
172
+ }
158
173
  }
159
174
 
160
175
  this.parent.transition('idle')
@@ -19,14 +19,20 @@ export class Rotating extends StateNode {
19
19
 
20
20
  snapshot = {} as TLRotationSnapshot
21
21
 
22
- info = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & { onInteractionEnd?: string }
22
+ info = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & {
23
+ onInteractionEnd?: string | (() => void)
24
+ }
23
25
 
24
26
  markId = ''
25
27
 
26
- override onEnter(info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string }) {
28
+ override onEnter(
29
+ info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string | (() => void) }
30
+ ) {
27
31
  // Store the event information
28
32
  this.info = info
29
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
33
+ if (typeof info.onInteractionEnd === 'string') {
34
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
35
+ }
30
36
 
31
37
  this.markId = this.editor.markHistoryStoppingPoint('rotate start')
32
38
 
@@ -121,11 +127,16 @@ export class Rotating extends StateNode {
121
127
  })
122
128
 
123
129
  this.editor.bailToMark(this.markId)
124
- if (this.info.onInteractionEnd) {
125
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
126
- } else {
127
- this.parent.transition('idle', this.info)
130
+ const { onInteractionEnd } = this.info
131
+ if (onInteractionEnd) {
132
+ if (typeof onInteractionEnd === 'string') {
133
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
134
+ } else {
135
+ onInteractionEnd()
136
+ }
137
+ return
128
138
  }
139
+ this.parent.transition('idle', this.info)
129
140
  }
130
141
 
131
142
  private complete() {
@@ -139,11 +150,16 @@ export class Rotating extends StateNode {
139
150
  this.editor,
140
151
  this.snapshot.shapeSnapshots.map((s) => s.shape.id)
141
152
  )
142
- if (this.info.onInteractionEnd) {
143
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
144
- } else {
145
- this.parent.transition('idle', this.info)
153
+ const { onInteractionEnd } = this.info
154
+ if (onInteractionEnd) {
155
+ if (typeof onInteractionEnd === 'string') {
156
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
157
+ } else {
158
+ onInteractionEnd()
159
+ }
160
+ return
146
161
  }
162
+ this.parent.transition('idle', this.info)
147
163
  }
148
164
 
149
165
  _getRotationFromPointerPosition({ snapToNearestDegree }: { snapToNearestDegree: boolean }) {
@@ -28,7 +28,7 @@ export type TranslatingInfo = TLPointerEventInfo & {
28
28
  isCreating?: boolean
29
29
  creatingMarkId?: string
30
30
  onCreate?(): void
31
- onInteractionEnd?: string
31
+ onInteractionEnd?: string | (() => void)
32
32
  }
33
33
 
34
34
  export class Translating extends StateNode {
@@ -59,7 +59,9 @@ export class Translating extends StateNode {
59
59
  }
60
60
 
61
61
  this.info = info
62
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
62
+ if (typeof info.onInteractionEnd === 'string') {
63
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
64
+ }
63
65
  this.isCreating = isCreating
64
66
 
65
67
  this.markId = ''
@@ -190,15 +192,24 @@ export class Translating extends StateNode {
190
192
  this.snapshot.movingShapes.map((s) => s.id)
191
193
  )
192
194
 
193
- if (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {
194
- this.editor.setCurrentTool(this.info.onInteractionEnd)
195
- } else {
196
- if (this.isCreating) {
197
- this.onCreate?.(this.editor.getOnlySelectedShape())
195
+ const { onInteractionEnd } = this.info
196
+ if (onInteractionEnd) {
197
+ if (typeof onInteractionEnd === 'string') {
198
+ if (this.editor.getInstanceState().isToolLocked) {
199
+ this.editor.setCurrentTool(onInteractionEnd)
200
+ return
201
+ }
198
202
  } else {
199
- this.parent.transition('idle')
203
+ onInteractionEnd()
204
+ return
200
205
  }
201
206
  }
207
+
208
+ if (this.isCreating) {
209
+ this.onCreate?.(this.editor.getOnlySelectedShape())
210
+ } else {
211
+ this.parent.transition('idle')
212
+ }
202
213
  }
203
214
 
204
215
  private cancel() {
@@ -214,11 +225,16 @@ export class Translating extends StateNode {
214
225
  })
215
226
 
216
227
  this.reset()
217
- if (this.info.onInteractionEnd) {
218
- this.editor.setCurrentTool(this.info.onInteractionEnd)
219
- } else {
220
- this.parent.transition('idle', this.info)
228
+ const { onInteractionEnd } = this.info
229
+ if (onInteractionEnd) {
230
+ if (typeof onInteractionEnd === 'string') {
231
+ this.editor.setCurrentTool(onInteractionEnd)
232
+ } else {
233
+ onInteractionEnd()
234
+ }
235
+ return
221
236
  }
237
+ this.parent.transition('idle', this.info)
222
238
  }
223
239
 
224
240
  protected handleStart() {
@@ -172,7 +172,7 @@ export function DebugFlags() {
172
172
  const items = Object.values(debugFlags)
173
173
  if (!items.length) return null
174
174
  return (
175
- <TldrawUiMenuSubmenu id="debug flags" label="Debug Flags">
175
+ <TldrawUiMenuSubmenu id="debug flags" label="Debug flags">
176
176
  <TldrawUiMenuGroup id="debug flags">
177
177
  {items.map((flag) => (
178
178
  <DebugFlagToggle key={flag.name} flag={flag} />
@@ -186,7 +186,7 @@ export function FeatureFlags() {
186
186
  const items = Object.values(featureFlags)
187
187
  if (!items.length) return null
188
188
  return (
189
- <TldrawUiMenuSubmenu id="feature flags" label="Feature Flags">
189
+ <TldrawUiMenuSubmenu id="feature flags" label="Feature flags">
190
190
  <TldrawUiMenuGroup id="feature flags">
191
191
  {items.map((flag) => (
192
192
  <DebugFlagToggle key={flag.name} flag={flag} />