tldraw 4.2.0-next.f100cedfc45b → 4.3.0-canary.03ae87dcc44b

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 (268) hide show
  1. package/dist-cjs/index.d.ts +17 -7
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +5 -2
  5. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  7. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  9. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
  10. package/dist-cjs/lib/shapes/arrow/elbow/elbowArrowSnapLines.js.map +2 -2
  11. package/dist-cjs/lib/shapes/arrow/shared.js.map +2 -2
  12. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  13. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
  14. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
  15. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +1 -1
  17. package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js +63 -36
  18. package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js.map +2 -2
  19. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  20. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  21. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +3 -3
  22. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  23. package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
  24. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  25. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  26. package/dist-cjs/lib/shapes/shared/ShapeFill.js +3 -0
  27. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  28. package/dist-cjs/lib/shapes/shared/crop.js +1 -0
  29. package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
  30. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  31. package/dist-cjs/lib/shapes/shared/useEditableRichText.js.map +2 -2
  32. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  33. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  34. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  35. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +1 -4
  36. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
  37. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
  38. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
  39. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +15 -7
  40. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  41. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
  42. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +2 -2
  43. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  44. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  45. package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
  46. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
  47. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  48. package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
  49. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  50. package/dist-cjs/lib/ui/components/Dialogs.js +2 -14
  51. package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
  52. package/dist-cjs/lib/ui/components/EditLinkDialog.js +11 -1
  53. package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
  54. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +5 -4
  55. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
  56. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  57. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js +2 -1
  58. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js.map +2 -2
  59. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  60. package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButton.js +2 -2
  61. package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButton.js.map +2 -2
  62. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +2 -1
  63. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  64. package/dist-cjs/lib/ui/context/actions.js +17 -2
  65. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  66. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  67. package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
  68. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +18 -16
  69. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +3 -3
  70. package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
  71. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  72. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  73. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +1 -0
  74. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  75. package/dist-cjs/lib/ui/hooks/useTranslation/useTranslation.js +1 -0
  76. package/dist-cjs/lib/ui/hooks/useTranslation/useTranslation.js.map +2 -2
  77. package/dist-cjs/lib/ui/version.js +3 -3
  78. package/dist-cjs/lib/ui/version.js.map +1 -1
  79. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +8 -0
  80. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  81. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  82. package/dist-cjs/lib/utils/frames/frames.js.map +2 -2
  83. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  84. package/dist-esm/index.d.mts +17 -7
  85. package/dist-esm/index.mjs +1 -1
  86. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
  87. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +6 -2
  88. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  89. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  90. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  91. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  92. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
  93. package/dist-esm/lib/shapes/arrow/elbow/elbowArrowSnapLines.mjs.map +2 -2
  94. package/dist-esm/lib/shapes/arrow/shared.mjs.map +2 -2
  95. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  96. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
  97. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
  98. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  99. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +1 -1
  100. package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs +65 -38
  101. package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs.map +2 -2
  102. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  103. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  104. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -5
  105. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  106. package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
  107. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  108. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  109. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +3 -0
  110. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  111. package/dist-esm/lib/shapes/shared/crop.mjs +1 -0
  112. package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
  113. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  114. package/dist-esm/lib/shapes/shared/useEditableRichText.mjs.map +2 -2
  115. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  116. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  117. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +1 -4
  118. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  119. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +1 -4
  120. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
  121. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
  122. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
  123. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +15 -7
  124. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  125. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
  126. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +2 -2
  127. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  128. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  129. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +1 -4
  130. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
  131. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
  132. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  133. package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
  134. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  135. package/dist-esm/lib/ui/components/Dialogs.mjs +2 -14
  136. package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
  137. package/dist-esm/lib/ui/components/EditLinkDialog.mjs +11 -1
  138. package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
  139. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +5 -5
  140. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
  141. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  142. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs +2 -1
  143. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs.map +2 -2
  144. package/dist-esm/lib/ui/components/menu-items.mjs +1 -4
  145. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  146. package/dist-esm/lib/ui/components/primitives/Button/TldrawUiButton.mjs +2 -2
  147. package/dist-esm/lib/ui/components/primitives/Button/TldrawUiButton.mjs.map +2 -2
  148. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -1
  149. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  150. package/dist-esm/lib/ui/context/actions.mjs +17 -2
  151. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  152. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  153. package/dist-esm/lib/ui/hooks/menu-hooks.mjs +1 -4
  154. package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
  155. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +18 -16
  156. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +3 -3
  157. package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
  158. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  159. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +1 -0
  160. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  161. package/dist-esm/lib/ui/hooks/useTranslation/useTranslation.mjs +1 -0
  162. package/dist-esm/lib/ui/hooks/useTranslation/useTranslation.mjs.map +2 -2
  163. package/dist-esm/lib/ui/version.mjs +3 -3
  164. package/dist-esm/lib/ui/version.mjs.map +1 -1
  165. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +8 -0
  166. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  167. package/dist-esm/lib/utils/export/exportAs.mjs +1 -3
  168. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  169. package/dist-esm/lib/utils/frames/frames.mjs.map +2 -2
  170. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  171. package/package.json +10 -10
  172. package/src/lib/bindings/arrow/ArrowBindingUtil.ts +1 -1
  173. package/src/lib/canvas/TldrawSelectionForeground.tsx +18 -7
  174. package/src/lib/defaultExternalContentHandlers.ts +3 -4
  175. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +2 -2
  176. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +1 -1
  177. package/src/lib/shapes/arrow/arrowLabel.ts +1 -1
  178. package/src/lib/shapes/arrow/arrowTargetState.ts +1 -1
  179. package/src/lib/shapes/arrow/elbow/elbowArrowSnapLines.tsx +3 -3
  180. package/src/lib/shapes/arrow/shared.ts +4 -4
  181. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +1 -1
  182. package/src/lib/shapes/bookmark/bookmarks.ts +3 -3
  183. package/src/lib/shapes/draw/toolStates/Drawing.ts +4 -4
  184. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  185. package/src/lib/shapes/frame/FrameShapeTool.ts +1 -1
  186. package/src/lib/shapes/frame/components/FrameLabelInput.tsx +48 -24
  187. package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +10 -2
  188. package/src/lib/shapes/geo/toolStates/Pointing.ts +3 -3
  189. package/src/lib/shapes/line/LineShapeTool.test.ts +6 -6
  190. package/src/lib/shapes/line/LineShapeUtil.test.tsx +5 -5
  191. package/src/lib/shapes/line/toolStates/Pointing.ts +1 -1
  192. package/src/lib/shapes/note/NoteShapeTool.test.ts +2 -1
  193. package/src/lib/shapes/note/NoteShapeUtil.tsx +6 -5
  194. package/src/lib/shapes/note/noteHelpers.ts +2 -2
  195. package/src/lib/shapes/shared/PlainTextLabel.tsx +2 -1
  196. package/src/lib/shapes/shared/RichTextLabel.tsx +2 -1
  197. package/src/lib/shapes/shared/ShapeFill.tsx +3 -0
  198. package/src/lib/shapes/shared/crop.ts +1 -0
  199. package/src/lib/shapes/shared/useEditablePlainText.ts +7 -3
  200. package/src/lib/shapes/shared/useEditableRichText.ts +7 -3
  201. package/src/lib/shapes/text/TextShapeTool.test.ts +4 -4
  202. package/src/lib/shapes/text/toolStates/Pointing.ts +1 -1
  203. package/src/lib/tools/EraserTool/childStates/Erasing.ts +3 -5
  204. package/src/lib/tools/EraserTool/childStates/Pointing.ts +3 -16
  205. package/src/lib/tools/SelectTool/DragAndDropManager.ts +2 -4
  206. package/src/lib/tools/SelectTool/childStates/Brushing.ts +2 -6
  207. package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +2 -3
  208. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +23 -15
  209. package/src/lib/tools/SelectTool/childStates/EditingShape.ts +2 -4
  210. package/src/lib/tools/SelectTool/childStates/Idle.ts +8 -12
  211. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +1 -1
  212. package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +4 -12
  213. package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +2 -2
  214. package/src/lib/tools/SelectTool/childStates/Resizing.ts +2 -4
  215. package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +2 -4
  216. package/src/lib/tools/SelectTool/childStates/Translating.ts +1 -3
  217. package/src/lib/ui/components/Dialogs.tsx +2 -14
  218. package/src/lib/ui/components/EditLinkDialog.tsx +16 -6
  219. package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +6 -5
  220. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -2
  221. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.tsx +4 -1
  222. package/src/lib/ui/components/menu-items.tsx +6 -14
  223. package/src/lib/ui/components/primitives/Button/TldrawUiButton.tsx +3 -2
  224. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +1 -0
  225. package/src/lib/ui/context/actions.tsx +25 -13
  226. package/src/lib/ui/context/events.tsx +1 -0
  227. package/src/lib/ui/hooks/menu-hooks.ts +9 -19
  228. package/src/lib/ui/hooks/useClipboardEvents.ts +12 -9
  229. package/src/lib/ui/hooks/useFlatten.ts +1 -2
  230. package/src/lib/ui/hooks/useTools.tsx +1 -2
  231. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +1 -0
  232. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +1 -0
  233. package/src/lib/ui/hooks/useTranslation/useTranslation.tsx +2 -1
  234. package/src/lib/ui/version.ts +3 -3
  235. package/src/lib/ui.css +4 -6
  236. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +8 -0
  237. package/src/lib/utils/export/exportAs.ts +2 -9
  238. package/src/lib/utils/frames/frames.ts +1 -1
  239. package/src/lib/utils/tldr/buildFromV1Document.ts +12 -17
  240. package/src/test/Editor.test.tsx +38 -12
  241. package/src/test/SelectTool.test.ts +11 -19
  242. package/src/test/TestEditor.ts +1 -4
  243. package/src/test/TldrawEditor.test.tsx +87 -40
  244. package/src/test/bindings.test.tsx +29 -25
  245. package/src/test/bindingsIndex.test.tsx +4 -4
  246. package/src/test/commands/createShape.test.ts +64 -0
  247. package/src/test/commands/createShapes.test.ts +15 -1
  248. package/src/test/commands/getSvgString.test.ts +2 -2
  249. package/src/test/commands/isShapeOfType.test.ts +44 -0
  250. package/src/test/commands/putContent.test.ts +1 -0
  251. package/src/test/commands/updateShape.test.ts +67 -0
  252. package/src/test/commands/updateShapes.test.ts +21 -5
  253. package/src/test/custom-clipping.test.ts +36 -35
  254. package/src/test/customSnapping.test.tsx +254 -54
  255. package/src/test/duplicate.test.ts +1 -1
  256. package/src/test/frames.test.ts +2 -2
  257. package/src/test/getCulledShapes.test.tsx +11 -3
  258. package/src/test/getShapeAtPoint.test.ts +2 -2
  259. package/src/test/groups.test.tsx +6 -3
  260. package/src/test/resizing.test.ts +9 -13
  261. package/src/test/selection-omnibus.test.ts +11 -11
  262. package/src/test/shapeutils.test.ts +1 -1
  263. package/src/test/styles2.test.tsx +1 -1
  264. package/src/test/styles3.test.ts +5 -5
  265. package/src/test/test-jsx.tsx +69 -57
  266. package/src/test/text.test.ts +15 -17
  267. package/src/test/translating.test.ts +6 -8
  268. package/tldraw.css +4 -6
@@ -83,28 +83,39 @@ export class DraggingHandle extends StateNode {
83
83
  // Find the adjacent handle
84
84
  this.initialAdjacentHandle = null
85
85
 
86
- // Start from the handle and work forward
87
- for (let i = index + 1; i < handles.length; i++) {
88
- const handle = handles[i]
89
- if (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {
90
- this.initialAdjacentHandle = handle
91
- break
86
+ // First, check if the handle specifies a custom reference handle
87
+ if (info.handle.snapReferenceHandleId) {
88
+ const customHandle = handles.find((h) => h.id === info.handle.snapReferenceHandleId)
89
+ if (customHandle) {
90
+ this.initialAdjacentHandle = customHandle
92
91
  }
93
92
  }
94
93
 
95
- // If still no handle, start from the end and work backward
94
+ // If no custom reference handle, use default behavior
96
95
  if (!this.initialAdjacentHandle) {
97
- for (let i = handles.length - 1; i >= 0; i--) {
96
+ // Start from the handle and work forward
97
+ for (let i = index + 1; i < handles.length; i++) {
98
98
  const handle = handles[i]
99
99
  if (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {
100
100
  this.initialAdjacentHandle = handle
101
101
  break
102
102
  }
103
103
  }
104
+
105
+ // If still no handle, start from the end and work backward
106
+ if (!this.initialAdjacentHandle) {
107
+ for (let i = handles.length - 1; i >= 0; i--) {
108
+ const handle = handles[i]
109
+ if (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {
110
+ this.initialAdjacentHandle = handle
111
+ break
112
+ }
113
+ }
114
+ }
104
115
  }
105
116
 
106
117
  // <!-- Only relevant to arrows
107
- if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
118
+ if (this.editor.isShapeOfType(shape, 'arrow')) {
108
119
  const initialBinding = getArrowBindings(this.editor, shape)[info.handle.id as 'start' | 'end']
109
120
 
110
121
  this.isPrecise = false
@@ -217,7 +228,7 @@ export class DraggingHandle extends StateNode {
217
228
  }
218
229
  const endChanges = util.onHandleDragEnd?.(shape, handleDragInfo)
219
230
  if (endChanges) {
220
- this.editor.updateShapes([{ ...endChanges, id: shape.id, type: shape.type }])
231
+ this.editor.updateShapes([{ ...endChanges, id: shape.id }])
221
232
  }
222
233
  }
223
234
 
@@ -284,7 +295,7 @@ export class DraggingHandle extends StateNode {
284
295
  if (!shape) return
285
296
  const util = editor.getShapeUtil(shape)
286
297
 
287
- const initialBinding = editor.isShapeOfType<TLArrowShape>(shape, 'arrow')
298
+ const initialBinding = editor.isShapeOfType(shape, 'arrow')
288
299
  ? getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']
289
300
  : undefined
290
301
 
@@ -341,10 +352,7 @@ export class DraggingHandle extends StateNode {
341
352
  const next: TLShapePartial<any> = { id: shape.id, type: shape.type, ...changes }
342
353
 
343
354
  // Arrows
344
- if (
345
- initialHandle.type === 'vertex' &&
346
- this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')
347
- ) {
355
+ if (initialHandle.type === 'vertex' && this.editor.isShapeOfType(shape, 'arrow')) {
348
356
  const bindingAfter = getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']
349
357
 
350
358
  if (bindingAfter) {
@@ -3,10 +3,8 @@ import {
3
3
  TLCancelEventInfo,
4
4
  TLCompleteEventInfo,
5
5
  tlenv,
6
- TLFrameShape,
7
6
  TLPointerEventInfo,
8
7
  TLShape,
9
- TLTextShape,
10
8
  } from '@tldraw/editor'
11
9
  import { getTextLabels } from '../../../utils/shapes/shapes'
12
10
  import { renderPlaintextFromRichText } from '../../../utils/text/richText'
@@ -109,7 +107,7 @@ export class EditingShape extends StateNode {
109
107
  const textLabel = textLabels.length === 1 ? textLabels[0] : undefined
110
108
  // N.B. One nuance here is that we want empty text fields to be removed from the canvas when the user clicks away from them.
111
109
  const isEmptyTextShape =
112
- this.editor.isShapeOfType<TLTextShape>(editingShape, 'text') &&
110
+ this.editor.isShapeOfType(editingShape, 'text') &&
113
111
  renderPlaintextFromRichText(this.editor, editingShape.props.richText).trim() === ''
114
112
  if (textLabel && !isEmptyTextShape) {
115
113
  const pointInShapeSpace = this.editor.getPointInShapeSpace(
@@ -135,7 +133,7 @@ export class EditingShape extends StateNode {
135
133
  } else {
136
134
  if (selectingShape.id === editingShape.id) {
137
135
  // If we clicked on a frame, while editing its heading, cancel editing
138
- if (this.editor.isShapeOfType<TLFrameShape>(selectingShape, 'frame')) {
136
+ if (this.editor.isShapeOfType(selectingShape, 'frame')) {
139
137
  this.editor.setEditingShape(null)
140
138
  this.parent.transition('idle', info)
141
139
  }
@@ -3,11 +3,9 @@ import {
3
3
  StateNode,
4
4
  TLAdjacentDirection,
5
5
  TLClickEventInfo,
6
- TLGroupShape,
7
6
  TLKeyboardEventInfo,
8
7
  TLPointerEventInfo,
9
8
  TLShape,
10
- TLTextShape,
11
9
  Vec,
12
10
  VecLike,
13
11
  createShapeId,
@@ -190,7 +188,7 @@ export class Idle extends StateNode {
190
188
  // unexpected results when working "inside of" a hollow shape.
191
189
 
192
190
  const hitShape =
193
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
191
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
194
192
  ? hoveredShape
195
193
  : (this.editor.getSelectedShapeAtPoint(this.editor.inputs.currentPagePoint) ??
196
194
  this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
@@ -201,13 +199,13 @@ export class Idle extends StateNode {
201
199
  const focusedGroupId = this.editor.getFocusedGroupId()
202
200
 
203
201
  if (hitShape) {
204
- if (this.editor.isShapeOfType<TLGroupShape>(hitShape, 'group')) {
202
+ if (this.editor.isShapeOfType(hitShape, 'group')) {
205
203
  // Probably select the shape
206
204
  selectOnCanvasPointerUp(this.editor, info)
207
205
  return
208
206
  } else {
209
207
  const parent = this.editor.getShape(hitShape.parentId)
210
- if (parent && this.editor.isShapeOfType<TLGroupShape>(parent, 'group')) {
208
+ if (parent && this.editor.isShapeOfType(parent, 'group')) {
211
209
  // The shape is the direct child of a group. If the group is
212
210
  // selected, then we can select the shape. If the group is the
213
211
  // focus layer id, then we can double click into it as usual.
@@ -356,7 +354,7 @@ export class Idle extends StateNode {
356
354
  case 'canvas': {
357
355
  const hoveredShape = this.editor.getHoveredShape()
358
356
  const hitShape =
359
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
357
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
360
358
  ? hoveredShape
361
359
  : this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
362
360
  margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(),
@@ -507,7 +505,7 @@ export class Idle extends StateNode {
507
505
  }
508
506
  case 'Tab': {
509
507
  const selectedShapes = this.editor.getSelectedShapes()
510
- if (selectedShapes.length) {
508
+ if (selectedShapes.length && !info.altKey) {
511
509
  this.editor.selectAdjacentShape(info.shiftKey ? 'prev' : 'next')
512
510
  }
513
511
  break
@@ -525,9 +523,7 @@ export class Idle extends StateNode {
525
523
  const selectedShapes = this.editor.getSelectedShapes()
526
524
 
527
525
  // On enter, if every selected shape is a group, then select all of the children of the groups
528
- if (
529
- selectedShapes.every((shape) => this.editor.isShapeOfType<TLGroupShape>(shape, 'group'))
530
- ) {
526
+ if (selectedShapes.every((shape) => this.editor.isShapeOfType(shape, 'group'))) {
531
527
  this.editor.setSelectedShapes(
532
528
  selectedShapes.flatMap((shape) => this.editor.getSortedChildIdsForParent(shape.id))
533
529
  )
@@ -557,7 +553,7 @@ export class Idle extends StateNode {
557
553
  }
558
554
  case 'Tab': {
559
555
  const selectedShapes = this.editor.getSelectedShapes()
560
- if (selectedShapes.length) {
556
+ if (selectedShapes.length && !info.altKey) {
561
557
  this.editor.selectAdjacentShape(info.shiftKey ? 'prev' : 'next')
562
558
  }
563
559
  break
@@ -604,7 +600,7 @@ export class Idle extends StateNode {
604
600
  const { x, y } = this.editor.inputs.currentPagePoint
605
601
 
606
602
  // Allow this to trigger the max shapes reached alert
607
- this.editor.createShapes<TLTextShape>([
603
+ this.editor.createShapes([
608
604
  {
609
605
  id,
610
606
  type: 'text',
@@ -124,7 +124,7 @@ export class PointingArrowLabel extends StateNode {
124
124
  }
125
125
 
126
126
  this.didDrag = true
127
- this.editor.updateShape<TLArrowShape>({
127
+ this.editor.updateShape({
128
128
  id: shape.id,
129
129
  type: shape.type,
130
130
  props: { labelPosition: nextLabelPosition },
@@ -1,12 +1,4 @@
1
- import {
2
- Editor,
3
- StateNode,
4
- TLArrowShape,
5
- TLHandle,
6
- TLNoteShape,
7
- TLPointerEventInfo,
8
- Vec,
9
- } from '@tldraw/editor'
1
+ import { Editor, StateNode, TLHandle, TLNoteShape, TLPointerEventInfo, Vec } from '@tldraw/editor'
10
2
  import { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'
11
3
  import { getArrowBindings } from '../../../shapes/arrow/shared'
12
4
  import {
@@ -29,7 +21,7 @@ export class PointingHandle extends StateNode {
29
21
  this.didCtrlOnEnter = info.accelKey
30
22
 
31
23
  const { shape } = info
32
- if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
24
+ if (this.editor.isShapeOfType(shape, 'arrow')) {
33
25
  const initialBindings = getArrowBindings(this.editor, shape)
34
26
  const currentBinding = initialBindings[info.handle.id as 'start' | 'end']
35
27
  const oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']
@@ -58,7 +50,7 @@ export class PointingHandle extends StateNode {
58
50
  override onPointerUp() {
59
51
  const { shape, handle } = this.info
60
52
 
61
- if (this.editor.isShapeOfType<TLNoteShape>(shape, 'note')) {
53
+ if (this.editor.isShapeOfType(shape, 'note')) {
62
54
  const { editor } = this
63
55
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)
64
56
  if (nextNote) {
@@ -90,7 +82,7 @@ export class PointingHandle extends StateNode {
90
82
  if (editor.getIsReadonly()) return
91
83
  const { shape, handle } = this.info
92
84
 
93
- if (editor.isShapeOfType<TLNoteShape>(shape, 'note')) {
85
+ if (editor.isShapeOfType(shape, 'note')) {
94
86
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)
95
87
  if (nextNote) {
96
88
  // Center the shape on the current pointer
@@ -1,4 +1,4 @@
1
- import { StateNode, TLClickEventInfo, TLGroupShape, TLPointerEventInfo } from '@tldraw/editor'
1
+ import { StateNode, TLClickEventInfo, TLPointerEventInfo } from '@tldraw/editor'
2
2
  import { selectOnCanvasPointerUp } from '../../selection-logic/selectOnCanvasPointerUp'
3
3
 
4
4
  export class PointingSelection extends StateNode {
@@ -35,7 +35,7 @@ export class PointingSelection extends StateNode {
35
35
  override onDoubleClick?(info: TLClickEventInfo) {
36
36
  const hoveredShape = this.editor.getHoveredShape()
37
37
  const hitShape =
38
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
38
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
39
39
  ? hoveredShape
40
40
  : this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
41
41
  hitInside: true,
@@ -7,12 +7,10 @@ import {
7
7
  SelectionCorner,
8
8
  SelectionEdge,
9
9
  StateNode,
10
- TLFrameShape,
11
10
  TLPointerEventInfo,
12
11
  TLShape,
13
12
  TLShapeId,
14
13
  TLShapePartial,
15
- TLTextShape,
16
14
  TLTickEventInfo,
17
15
  Vec,
18
16
  VecLike,
@@ -228,7 +226,7 @@ export class Resizing extends StateNode {
228
226
 
229
227
  if (shapeSnapshots.size === 1) {
230
228
  const onlySnapshot = [...shapeSnapshots.values()][0]!
231
- if (this.editor.isShapeOfType<TLTextShape>(onlySnapshot.shape, 'text')) {
229
+ if (this.editor.isShapeOfType(onlySnapshot.shape, 'text')) {
232
230
  isAspectRatioLocked = !(this.info.handle === 'left' || this.info.handle === 'right')
233
231
  }
234
232
  }
@@ -528,7 +526,7 @@ export class Resizing extends StateNode {
528
526
  // descendants (easy) but also flagging with behavior like "resize" or "keep absolute position" or "reposition only with accel key",
529
527
  // though I'm not sure where that would be defined; perhaps better handled with onResizeStart / onResize callbacks on the util, and
530
528
  // pass `accelKeyIsPressed` as well as `accelKeyWasPressed`?
531
- if (editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {
529
+ if (editor.isShapeOfType(shape, 'frame')) {
532
530
  frames.push({
533
531
  id: shape.id,
534
532
  children: compact(
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  Geometry2d,
3
3
  StateNode,
4
- TLFrameShape,
5
- TLGroupShape,
6
4
  TLShape,
7
5
  TLShapeId,
8
6
  Vec,
@@ -104,7 +102,7 @@ export class ScribbleBrushing extends StateNode {
104
102
 
105
103
  // If the shape is a group or is already selected or locked, don't select it
106
104
  if (
107
- editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
105
+ editor.isShapeOfType(shape, 'group') ||
108
106
  newlySelectedShapeIds.has(shape.id) ||
109
107
  editor.isShapeOrAncestorLocked(shape)
110
108
  ) {
@@ -115,7 +113,7 @@ export class ScribbleBrushing extends StateNode {
115
113
 
116
114
  // If the scribble started inside of the frame, don't select it
117
115
  if (
118
- editor.isShapeOfType<TLFrameShape>(shape, 'frame') &&
116
+ editor.isShapeOfType(shape, 'frame') &&
119
117
  geometry.bounds.containsPoint(editor.getPointInShapeSpace(shape, originPagePoint))
120
118
  ) {
121
119
  continue
@@ -407,9 +407,7 @@ function getTranslatingSnapshot(editor: Editor) {
407
407
  const { originPagePoint } = editor.inputs
408
408
 
409
409
  const allHoveredNotes = shapeSnapshots.filter(
410
- (s) =>
411
- editor.isShapeOfType<TLNoteShape>(s.shape, 'note') &&
412
- editor.isPointInShape(s.shape, originPagePoint)
410
+ (s) => editor.isShapeOfType(s.shape, 'note') && editor.isPointInShape(s.shape, originPagePoint)
413
411
  ) as (MovingShapeSnapshot & { shape: TLNoteShape })[]
414
412
 
415
413
  if (allHoveredNotes.length === 0) {
@@ -4,12 +4,7 @@ import { memo, useCallback, useRef } from 'react'
4
4
  import { TLUiDialog, useDialogs } from '../context/dialogs'
5
5
 
6
6
  /** @internal */
7
- const TldrawUiDialog = ({
8
- id,
9
- component: ModalContent,
10
- onClose,
11
- preventBackgroundClose,
12
- }: TLUiDialog) => {
7
+ const TldrawUiDialog = ({ id, component: ModalContent, preventBackgroundClose }: TLUiDialog) => {
13
8
  const { removeDialog } = useDialogs()
14
9
  const mouseDownInsideContentRef = useRef(false)
15
10
 
@@ -18,17 +13,10 @@ const TldrawUiDialog = ({
18
13
  const handleOpenChange = useCallback(
19
14
  (isOpen: boolean) => {
20
15
  if (!isOpen) {
21
- if (onClose) {
22
- try {
23
- onClose()
24
- } catch (err: any) {
25
- console.warn(err)
26
- }
27
- }
28
16
  removeDialog(id)
29
17
  }
30
18
  },
31
- [id, onClose, removeDialog]
19
+ [id, removeDialog]
32
20
  )
33
21
 
34
22
  return (
@@ -1,4 +1,4 @@
1
- import { T, TLBaseShape, track, useEditor } from '@tldraw/editor'
1
+ import { ExtractShapeByProps, T, TLShape, track, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { TLUiDialogProps } from '../context/dialogs'
4
4
  import { useTranslation } from '../hooks/useTranslation/useTranslation'
@@ -25,20 +25,28 @@ function validateUrl(url: string) {
25
25
  return { isValid: false, hasProtocol: false }
26
26
  }
27
27
 
28
- type ShapeWithUrl = TLBaseShape<string, { url: string }>
28
+ type ShapeWithUrl = ExtractShapeByProps<{ url: string }>
29
+
30
+ function isShapeWithUrl(shape: TLShape | null | undefined): shape is ShapeWithUrl {
31
+ return !!(shape && 'url' in shape.props && typeof shape.props.url === 'string')
32
+ }
33
+
34
+ function assertShapeWithUrl(shape: TLShape | null | undefined): asserts shape is ShapeWithUrl {
35
+ if (!isShapeWithUrl(shape)) {
36
+ throw new Error('Shape is not a valid ShapeWithUrl')
37
+ }
38
+ }
29
39
 
30
40
  export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
31
41
  const editor = useEditor()
32
42
 
33
43
  const selectedShape = editor.getOnlySelectedShape()
34
44
 
35
- if (
36
- !(selectedShape && 'url' in selectedShape.props && typeof selectedShape.props.url === 'string')
37
- ) {
45
+ if (!isShapeWithUrl(selectedShape)) {
38
46
  return null
39
47
  }
40
48
 
41
- return <EditLinkDialogInner onClose={onClose} selectedShape={selectedShape as ShapeWithUrl} />
49
+ return <EditLinkDialogInner onClose={onClose} selectedShape={selectedShape} />
42
50
  })
43
51
 
44
52
  export const EditLinkDialogInner = track(function EditLinkDialogInner({
@@ -98,6 +106,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
98
106
  const handleClear = useCallback(() => {
99
107
  const onlySelectedShape = editor.getOnlySelectedShape()
100
108
  if (!onlySelectedShape) return
109
+ assertShapeWithUrl(onlySelectedShape)
101
110
  editor.updateShapes([
102
111
  { id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: '' } },
103
112
  ])
@@ -108,6 +117,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
108
117
  const onlySelectedShape = editor.getOnlySelectedShape()
109
118
 
110
119
  if (!onlySelectedShape) return
120
+ assertShapeWithUrl(onlySelectedShape)
111
121
 
112
122
  // ? URL is a magic value
113
123
  if (onlySelectedShape && 'url' in onlySelectedShape.props) {
@@ -3,7 +3,6 @@ import {
3
3
  TLPageId,
4
4
  releasePointerCapture,
5
5
  setPointerCapture,
6
- tlenv,
7
6
  useEditor,
8
7
  useValue,
9
8
  } from '@tldraw/editor'
@@ -306,6 +305,8 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
306
305
  [editor, trackEvent]
307
306
  )
308
307
 
308
+ const shouldUseWindowPrompt = breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && isCoarsePointer
309
+
309
310
  return (
310
311
  <TldrawUiPopover id="pages" onOpenChange={onOpenChange} open={isOpen}>
311
312
  <TldrawUiPopoverTrigger data-testid="main.page-menu">
@@ -390,7 +391,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
390
391
  >
391
392
  <TldrawUiButtonIcon icon="drag-handle-dots" />
392
393
  </TldrawUiButton>
393
- {breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && isCoarsePointer ? (
394
+ {shouldUseWindowPrompt ? (
394
395
  // sigh, this is a workaround for iOS Safari
395
396
  // because the device and the radix popover seem
396
397
  // to be fighting over scroll position. Nothing
@@ -399,7 +400,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
399
400
  type="normal"
400
401
  className="tlui-page-menu__item__button"
401
402
  onClick={() => {
402
- const name = window.prompt('Rename page', page.name)
403
+ const name = window.prompt(msg('action.rename'), page.name)
403
404
  if (name && name !== page.name) {
404
405
  renamePage(page.id, name)
405
406
  }
@@ -465,8 +466,8 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
465
466
  item={page}
466
467
  listSize={pages.length}
467
468
  onRename={() => {
468
- if (tlenv.isIos) {
469
- const name = window.prompt('Rename page', page.name)
469
+ if (shouldUseWindowPrompt) {
470
+ const name = window.prompt(msg('action.rename'), page.name)
470
471
  if (name && name !== page.name) {
471
472
  renamePage(page.id, name)
472
473
  }
@@ -1,4 +1,4 @@
1
- import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
1
+ import { ExtractShapeByProps, preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
@@ -31,7 +31,7 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
31
31
 
32
32
  const handleComplete = () => {
33
33
  trackEvent('set-alt-text', { source })
34
- const shape = editor.getShape<TLShape & { props: { altText: string } }>(shapeId)
34
+ const shape = editor.getShape<ExtractShapeByProps<{ altText: string }>>(shapeId)
35
35
  if (!shape) return
36
36
  editor.updateShapes([
37
37
  {
@@ -54,6 +54,9 @@ export function DefaultRichTextToolbarContent({
54
54
  // todo: we could make this a prop
55
55
  const actions = useMemo(() => {
56
56
  function handleOp(name: string, op: string) {
57
+ // Check if the editor view is available before calling operations
58
+ if (!textEditor.view) return
59
+
57
60
  trackEvent('rich-text', { operation: name as any, source })
58
61
  // @ts-expect-error typing this is annoying at the moment.
59
62
  textEditor.chain().focus()[op]().run()
@@ -109,7 +112,7 @@ export function DefaultRichTextToolbarContent({
109
112
  }, [textEditor, trackEvent, onEditLinkStart])
110
113
 
111
114
  return actions.map(({ name, attrs, onSelect }) => {
112
- const isActive = textEditor.isActive(name, attrs)
115
+ const isActive = textEditor.view ? textEditor.isActive(name, attrs) : false
113
116
  return (
114
117
  <TldrawUiToolbarButton
115
118
  key={name}
@@ -1,12 +1,4 @@
1
- import {
2
- TLBookmarkShape,
3
- TLEmbedShape,
4
- TLFrameShape,
5
- TLImageShape,
6
- TLPageId,
7
- useEditor,
8
- useValue,
9
- } from '@tldraw/editor'
1
+ import { TLPageId, useEditor, useValue } from '@tldraw/editor'
10
2
  import { supportsDownloadingOriginal } from '../context/actions'
11
3
  import { useUiEvents } from '../context/events'
12
4
  import { useToasts } from '../context/toasts'
@@ -64,7 +56,7 @@ export function FlattenMenuItem() {
64
56
  const selectedShapeIds = editor.getSelectedShapeIds()
65
57
  if (selectedShapeIds.length === 0) return false
66
58
  const onlySelectedShape = editor.getOnlySelectedShape()
67
- if (onlySelectedShape && editor.isShapeOfType<TLImageShape>(onlySelectedShape, 'image')) {
59
+ if (onlySelectedShape && editor.isShapeOfType(onlySelectedShape, 'image')) {
68
60
  return false
69
61
  }
70
62
  return true
@@ -117,7 +109,7 @@ export function RemoveFrameMenuItem() {
117
109
  () => {
118
110
  const selectedShapes = editor.getSelectedShapes()
119
111
  if (selectedShapes.length === 0) return false
120
- return selectedShapes.every((shape) => editor.isShapeOfType<TLFrameShape>(shape, 'frame'))
112
+ return selectedShapes.every((shape) => editor.isShapeOfType(shape, 'frame'))
121
113
  },
122
114
  [editor]
123
115
  )
@@ -135,7 +127,7 @@ export function FitFrameToContentMenuItem() {
135
127
  const onlySelectedShape = editor.getOnlySelectedShape()
136
128
  if (!onlySelectedShape) return false
137
129
  return (
138
- editor.isShapeOfType<TLFrameShape>(onlySelectedShape, 'frame') &&
130
+ editor.isShapeOfType(onlySelectedShape, 'frame') &&
139
131
  editor.getSortedChildIdsForParent(onlySelectedShape).length > 0
140
132
  )
141
133
  },
@@ -518,7 +510,7 @@ export function ConvertToBookmarkMenuItem() {
518
510
  const onlySelectedShape = editor.getOnlySelectedShape()
519
511
  if (!onlySelectedShape) return false
520
512
  return !!(
521
- editor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&
513
+ editor.isShapeOfType(onlySelectedShape, 'embed') &&
522
514
  onlySelectedShape.props.url &&
523
515
  !editor.isShapeOrAncestorLocked(onlySelectedShape)
524
516
  )
@@ -542,7 +534,7 @@ export function ConvertToEmbedMenuItem() {
542
534
  const onlySelectedShape = editor.getOnlySelectedShape()
543
535
  if (!onlySelectedShape) return false
544
536
  return !!(
545
- editor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&
537
+ editor.isShapeOfType(onlySelectedShape, 'bookmark') &&
546
538
  onlySelectedShape.props.url &&
547
539
  getEmbedDefinition(onlySelectedShape.props.url) &&
548
540
  !editor.isShapeOrAncestorLocked(onlySelectedShape)
@@ -6,6 +6,7 @@ export interface TLUiButtonProps extends React.HTMLAttributes<HTMLButtonElement>
6
6
  disabled?: boolean
7
7
  isActive?: boolean
8
8
  type: 'normal' | 'primary' | 'danger' | 'low' | 'icon' | 'tool' | 'menu' | 'help'
9
+ htmlButtonType?: 'button' | 'submit' | 'reset'
9
10
  }
10
11
 
11
12
  const namedClassNamesSoThatICanGrepForThis = {
@@ -21,11 +22,11 @@ const namedClassNamesSoThatICanGrepForThis = {
21
22
 
22
23
  /** @public @react */
23
24
  export const TldrawUiButton = React.forwardRef<HTMLButtonElement, TLUiButtonProps>(
24
- function TldrawUiButton({ children, type, isActive, ...props }, ref) {
25
+ function TldrawUiButton({ children, type, htmlButtonType, isActive, ...props }, ref) {
25
26
  return (
26
27
  <button
27
28
  ref={ref}
28
- type="button"
29
+ type={htmlButtonType || 'button'}
29
30
  draggable={false}
30
31
  data-isactive={isActive}
31
32
  {...props}
@@ -134,6 +134,7 @@ export function TldrawUiMenuItem<
134
134
  {iconLeft && <TldrawUiButtonIcon icon={iconLeft} small />}
135
135
  <TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
136
136
  {kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}
137
+ {icon && <TldrawUiButtonIcon icon={icon} small />}
137
138
  </TldrawUiButton>
138
139
  </TldrawUiDropdownMenuItem>
139
140
  )