tldraw 3.15.1 → 3.16.0-canary.03ed24d72068

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 (360) hide show
  1. package/dist-cjs/index.d.ts +198 -9
  2. package/dist-cjs/index.js +24 -2
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawCropHandles.js.map +2 -2
  5. package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
  6. package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js +1 -0
  8. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  9. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +25 -39
  10. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  11. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +16 -4
  12. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  13. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js +1 -1
  14. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
  15. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
  16. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  17. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
  18. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  19. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
  20. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  21. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  22. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  23. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +12 -12
  24. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  25. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  26. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  27. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +2 -2
  28. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js +2 -1
  30. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js.map +2 -2
  31. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +5 -1
  32. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  33. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
  34. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
  35. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +20 -2
  36. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  37. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +4 -4
  38. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  39. package/dist-cjs/lib/shapes/shared/ShapeFill.js +5 -5
  40. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  41. package/dist-cjs/lib/shapes/shared/usePrefersReducedMotion.js +10 -1
  42. package/dist-cjs/lib/shapes/shared/usePrefersReducedMotion.js.map +2 -2
  43. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
  44. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  45. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  46. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  47. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +43 -22
  48. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  49. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +2 -15
  50. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  51. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +5 -0
  52. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  53. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +8 -0
  54. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  55. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +8 -0
  56. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  57. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +8 -0
  58. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  59. package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js.map +2 -2
  60. package/dist-cjs/lib/ui/TldrawUi.js +14 -0
  61. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  62. package/dist-cjs/lib/ui/components/AccessibilityMenu.js +35 -0
  63. package/dist-cjs/lib/ui/components/AccessibilityMenu.js.map +7 -0
  64. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +12 -3
  65. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  66. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js +3 -2
  67. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js.map +2 -2
  68. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +40 -0
  69. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
  70. package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js +3 -3
  71. package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js.map +2 -2
  72. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  73. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  74. package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
  75. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  76. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +1 -1
  77. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
  78. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +2 -1
  79. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
  80. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js +3 -2
  81. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js.map +2 -2
  82. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js +2 -2
  83. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js.map +2 -2
  84. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +2 -0
  85. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  86. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +171 -140
  87. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  88. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js +3 -3
  89. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +2 -2
  90. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js +26 -25
  91. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +3 -3
  92. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
  93. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -21
  95. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  96. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +189 -80
  97. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  98. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +5 -4
  99. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
  100. package/dist-cjs/lib/ui/components/menu-items.js +6 -0
  101. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  102. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js +5 -16
  103. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +3 -3
  104. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +1 -1
  105. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  106. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +3 -2
  107. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +3 -3
  108. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -0
  109. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  110. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +30 -7
  111. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  112. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +253 -0
  113. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +7 -0
  114. package/dist-cjs/lib/ui/components/primitives/layout.js +76 -0
  115. package/dist-cjs/lib/ui/components/primitives/layout.js.map +7 -0
  116. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  117. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  118. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  119. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +153 -20
  120. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  121. package/dist-cjs/lib/ui/context/TldrawUiContextProvider.js +3 -2
  122. package/dist-cjs/lib/ui/context/TldrawUiContextProvider.js.map +2 -2
  123. package/dist-cjs/lib/ui/context/actions.js +29 -7
  124. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  125. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  126. package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
  127. package/dist-cjs/lib/ui/hooks/useTools.js +94 -9
  128. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  129. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  130. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +7 -0
  131. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  132. package/dist-cjs/lib/ui/kbd-utils.js +2 -1
  133. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  134. package/dist-cjs/lib/ui/version.js +3 -3
  135. package/dist-cjs/lib/ui/version.js.map +1 -1
  136. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +1 -1
  137. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  138. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js +3 -2
  139. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  140. package/dist-esm/index.d.mts +198 -9
  141. package/dist-esm/index.mjs +40 -3
  142. package/dist-esm/index.mjs.map +2 -2
  143. package/dist-esm/lib/canvas/TldrawCropHandles.mjs.map +2 -2
  144. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  145. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  146. package/dist-esm/lib/defaultExternalContentHandlers.mjs +1 -0
  147. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  148. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +28 -39
  149. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  150. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +19 -5
  151. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  152. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs +1 -1
  153. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
  154. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  155. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  156. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +3 -0
  157. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  158. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
  159. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  160. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  161. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  162. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +13 -12
  163. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  164. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  165. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  166. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
  167. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  168. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
  169. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
  170. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
  171. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  172. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
  173. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
  174. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +21 -2
  175. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  176. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
  177. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  178. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +6 -5
  179. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  180. package/dist-esm/lib/shapes/shared/usePrefersReducedMotion.mjs +10 -1
  181. package/dist-esm/lib/shapes/shared/usePrefersReducedMotion.mjs.map +2 -2
  182. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
  183. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  184. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  185. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  186. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +43 -22
  187. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  188. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +2 -15
  189. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  190. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +5 -0
  191. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  192. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +8 -0
  193. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  194. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +8 -0
  195. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  196. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +8 -0
  197. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  198. package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs.map +2 -2
  199. package/dist-esm/lib/ui/TldrawUi.mjs +16 -2
  200. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  201. package/dist-esm/lib/ui/components/AccessibilityMenu.mjs +19 -0
  202. package/dist-esm/lib/ui/components/AccessibilityMenu.mjs.map +7 -0
  203. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +12 -3
  204. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  205. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs +3 -2
  206. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs.map +2 -2
  207. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +40 -0
  208. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
  209. package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs +3 -5
  210. package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs.map +2 -2
  211. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  212. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  213. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
  214. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  215. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +1 -1
  216. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
  217. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +2 -1
  218. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
  219. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs +3 -2
  220. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs.map +2 -2
  221. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs +2 -2
  222. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs.map +2 -2
  223. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +3 -1
  224. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  225. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +171 -140
  226. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  227. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs +3 -3
  228. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +2 -2
  229. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs +26 -25
  230. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +2 -2
  231. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
  232. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  233. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -21
  234. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  235. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -81
  236. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  237. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +5 -4
  238. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
  239. package/dist-esm/lib/ui/components/menu-items.mjs +6 -0
  240. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  241. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +6 -6
  242. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +2 -2
  243. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +1 -1
  244. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  245. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +3 -2
  246. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
  247. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +1 -0
  248. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  249. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +30 -7
  250. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  251. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +230 -0
  252. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +7 -0
  253. package/dist-esm/lib/ui/components/primitives/layout.mjs +46 -0
  254. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +7 -0
  255. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  256. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  257. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  258. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +161 -22
  259. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  260. package/dist-esm/lib/ui/context/TldrawUiContextProvider.mjs +3 -2
  261. package/dist-esm/lib/ui/context/TldrawUiContextProvider.mjs.map +2 -2
  262. package/dist-esm/lib/ui/context/actions.mjs +29 -7
  263. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  264. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  265. package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
  266. package/dist-esm/lib/ui/hooks/useTools.mjs +102 -10
  267. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  268. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +7 -0
  269. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  270. package/dist-esm/lib/ui/kbd-utils.mjs +2 -1
  271. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  272. package/dist-esm/lib/ui/version.mjs +3 -3
  273. package/dist-esm/lib/ui/version.mjs.map +1 -1
  274. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +1 -1
  275. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  276. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs +3 -2
  277. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  278. package/package.json +3 -3
  279. package/src/index.ts +29 -1
  280. package/src/lib/canvas/TldrawCropHandles.tsx +2 -0
  281. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  282. package/src/lib/defaultExternalContentHandlers.ts +2 -1
  283. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +5 -5
  284. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +29 -42
  285. package/src/lib/shapes/arrow/arrowLabel.ts +23 -3
  286. package/src/lib/shapes/arrow/arrowTargetState.ts +2 -1
  287. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  288. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +3 -0
  289. package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
  290. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  291. package/src/lib/shapes/frame/FrameShapeUtil.tsx +21 -14
  292. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  293. package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
  294. package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
  295. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
  296. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
  297. package/src/lib/shapes/line/LineShapeUtil.tsx +25 -3
  298. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
  299. package/src/lib/shapes/shared/ShapeFill.tsx +6 -5
  300. package/src/lib/shapes/shared/usePrefersReducedMotion.tsx +11 -1
  301. package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
  302. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  303. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +54 -30
  304. package/src/lib/tools/SelectTool/childStates/Idle.ts +2 -24
  305. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +7 -0
  306. package/src/lib/tools/SelectTool/childStates/Resizing.ts +12 -1
  307. package/src/lib/tools/SelectTool/childStates/Rotating.ts +11 -0
  308. package/src/lib/tools/SelectTool/childStates/Translating.ts +11 -1
  309. package/src/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.ts +1 -0
  310. package/src/lib/ui/TldrawUi.tsx +17 -2
  311. package/src/lib/ui/components/AccessibilityMenu.tsx +20 -0
  312. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +15 -3
  313. package/src/lib/ui/components/DefaultMenuPanel.tsx +4 -3
  314. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +32 -0
  315. package/src/lib/ui/components/MainMenu/DefaultMainMenuContent.tsx +4 -4
  316. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  317. package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
  318. package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +1 -1
  319. package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +3 -2
  320. package/src/lib/ui/components/SharePanel/PeopleMenuItem.tsx +4 -3
  321. package/src/lib/ui/components/SharePanel/UserPresenceColorPicker.tsx +3 -3
  322. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +3 -1
  323. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +146 -107
  324. package/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx +3 -3
  325. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +7 -6
  326. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
  327. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -23
  328. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +212 -61
  329. package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +17 -12
  330. package/src/lib/ui/components/menu-items.tsx +8 -0
  331. package/src/lib/ui/components/primitives/TldrawUiButtonPicker.tsx +40 -37
  332. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +1 -1
  333. package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +4 -2
  334. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +1 -0
  335. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +51 -12
  336. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +301 -0
  337. package/src/lib/ui/components/primitives/layout.tsx +107 -0
  338. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  339. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  340. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +220 -19
  341. package/src/lib/ui/context/TldrawUiContextProvider.tsx +23 -20
  342. package/src/lib/ui/context/actions.tsx +29 -7
  343. package/src/lib/ui/context/events.tsx +4 -2
  344. package/src/lib/ui/hooks/menu-hooks.ts +1 -0
  345. package/src/lib/ui/hooks/useTools.tsx +138 -10
  346. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +7 -0
  347. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +7 -0
  348. package/src/lib/ui/kbd-utils.ts +2 -1
  349. package/src/lib/ui/version.ts +3 -3
  350. package/src/lib/ui.css +406 -292
  351. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +16 -2
  352. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +1 -1
  353. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +24 -3
  354. package/src/lib/utils/tldr/buildFromV1Document.ts +2 -1
  355. package/src/test/SelectTool.test.ts +37 -11
  356. package/src/test/arrows-megabus.test.tsx +12 -6
  357. package/src/test/commands/deletePage.test.ts +84 -1
  358. package/src/test/inner-outer-margin.test.ts +315 -0
  359. package/src/test/shapeutils.test.ts +394 -45
  360. package/tldraw.css +703 -603
@@ -149,13 +149,20 @@ exports[`putExcalidrawContent test fixtures bound-arrows.json 1`] = `
149
149
  "kind": "arc",
150
150
  "labelColor": "black",
151
151
  "labelPosition": 0.5,
152
+ "richText": {
153
+ "content": [
154
+ {
155
+ "type": "paragraph",
156
+ },
157
+ ],
158
+ "type": "doc",
159
+ },
152
160
  "scale": 1,
153
161
  "size": "m",
154
162
  "start": {
155
163
  "x": 0,
156
164
  "y": 0,
157
165
  },
158
- "text": "",
159
166
  },
160
167
  "rotation": 0,
161
168
  "type": "arrow",
@@ -315,13 +322,20 @@ exports[`putExcalidrawContent test fixtures bound-elbow-arrows.json 1`] = `
315
322
  "kind": "elbow",
316
323
  "labelColor": "black",
317
324
  "labelPosition": 0.5,
325
+ "richText": {
326
+ "content": [
327
+ {
328
+ "type": "paragraph",
329
+ },
330
+ ],
331
+ "type": "doc",
332
+ },
318
333
  "scale": 1,
319
334
  "size": "m",
320
335
  "start": {
321
336
  "x": 0,
322
337
  "y": 0,
323
338
  },
324
- "text": "",
325
339
  },
326
340
  "rotation": 0,
327
341
  "type": "arrow",
@@ -221,7 +221,7 @@ export async function putExcalidrawContent(
221
221
  ...base,
222
222
  type: 'arrow',
223
223
  props: {
224
- text,
224
+ richText: toRichText(text),
225
225
  kind: element.elbowed ? 'elbow' : 'arc',
226
226
  bend: getBend(element, start, end),
227
227
  dash: getDash(element),
@@ -112,13 +112,20 @@ exports[`buildFromV1Document test fixtures arrow-binding.tldr 1`] = `
112
112
  "kind": "arc",
113
113
  "labelColor": "red",
114
114
  "labelPosition": 0.5,
115
+ "richText": {
116
+ "content": [
117
+ {
118
+ "type": "paragraph",
119
+ },
120
+ ],
121
+ "type": "doc",
122
+ },
115
123
  "scale": 1,
116
124
  "size": "m",
117
125
  "start": {
118
126
  "x": 146.32,
119
127
  "y": 0,
120
128
  },
121
- "text": "",
122
129
  },
123
130
  "rotation": 0,
124
131
  "type": "arrow",
@@ -241,13 +248,20 @@ exports[`buildFromV1Document test fixtures exact-arrow-binding.tldr 1`] = `
241
248
  "kind": "arc",
242
249
  "labelColor": "red",
243
250
  "labelPosition": 0.5,
251
+ "richText": {
252
+ "content": [
253
+ {
254
+ "type": "paragraph",
255
+ },
256
+ ],
257
+ "type": "doc",
258
+ },
244
259
  "scale": 1,
245
260
  "size": "m",
246
261
  "start": {
247
262
  "x": 293.36,
248
263
  "y": 0,
249
264
  },
250
- "text": "",
251
265
  },
252
266
  "rotation": 0,
253
267
  "type": "arrow",
@@ -389,13 +403,20 @@ exports[`buildFromV1Document test fixtures incorrect-arrow-binding.tldr 1`] = `
389
403
  "kind": "arc",
390
404
  "labelColor": "red",
391
405
  "labelPosition": 0.5,
406
+ "richText": {
407
+ "content": [
408
+ {
409
+ "type": "paragraph",
410
+ },
411
+ ],
412
+ "type": "doc",
413
+ },
392
414
  "scale": 1,
393
415
  "size": "m",
394
416
  "start": {
395
417
  "x": 252.64,
396
418
  "y": 0,
397
419
  },
398
- "text": "",
399
420
  },
400
421
  "rotation": 0,
401
422
  "type": "arrow",
@@ -405,7 +405,7 @@ export function buildFromV1Document(editor: Editor, _document: unknown) {
405
405
  ...inCommon,
406
406
  type: 'arrow',
407
407
  props: {
408
- text: v1Shape.label ?? '',
408
+ richText: toRichText(v1Shape.label ?? ''),
409
409
  color: getV2Color(v1Shape.style.color),
410
410
  labelColor: getV2Color(v1Shape.style.color),
411
411
  size: getV2Size(v1Shape.style.size),
@@ -562,6 +562,7 @@ export function buildFromV1Document(editor: Editor, _document: unknown) {
562
562
  y: point.y,
563
563
  },
564
564
  isPrecise: point.x !== 0.5 || point.y !== 0.5,
565
+ isCreatingShape: true,
565
566
  })
566
567
 
567
568
  if (change) {
@@ -275,17 +275,27 @@ describe('PointingLabel', () => {
275
275
  type: 'arrow',
276
276
  x: 100,
277
277
  y: 100,
278
- props: { text: 'Test Label', end: { x: 100, y: 100 } },
278
+ props: {
279
+ richText: toRichText('Test Label'),
280
+ start: { x: 0, y: 0 },
281
+ end: { x: 100, y: 0 },
282
+ },
279
283
  },
280
284
  ])
281
- const shape = editor.getShape(ids.arrow1)
282
- editor.pointerDown(150, 150, {
285
+ const shape = editor.getShape(ids.arrow1)!
286
+ // First select the shape so it's already selected
287
+ editor.select(shape.id)
288
+
289
+ // Click at the middle of the arrow where the label would be and drag to move the label
290
+ editor.pointerDown(150, 100, {
283
291
  target: 'shape',
284
292
  shape,
285
293
  })
286
- editor.pointerMove(100, 100)
294
+ editor.pointerMove(160, 100)
287
295
  editor.expectToBeIn('select.pointing_arrow_label')
288
296
 
297
+ // Continue dragging to actually move the label, then it should go to idle
298
+ editor.pointerMove(170, 100)
289
299
  editor.pointerUp()
290
300
  editor.expectToBeIn('select.idle')
291
301
  })
@@ -297,16 +307,21 @@ describe('PointingLabel', () => {
297
307
  type: 'arrow',
298
308
  x: 100,
299
309
  y: 100,
300
- props: { text: 'Test Label', end: { x: 100, y: 100 } },
310
+ props: {
311
+ richText: toRichText('Test Label'),
312
+ start: { x: 0, y: 0 },
313
+ end: { x: 100, y: 0 },
314
+ },
301
315
  },
302
316
  ])
303
317
  const shape = editor.getShape(ids.arrow1)
304
318
 
305
- editor.pointerDown(150, 150, {
319
+ // Click at the middle of the arrow where the label would be
320
+ editor.pointerDown(150, 100, {
306
321
  target: 'shape',
307
322
  shape,
308
323
  })
309
- editor.pointerMove(100, 100)
324
+ editor.pointerMove(160, 100)
310
325
  editor.expectToBeIn('select.pointing_arrow_label')
311
326
  editor.cancel()
312
327
  editor.expectToBeIn('select.idle')
@@ -314,14 +329,25 @@ describe('PointingLabel', () => {
314
329
 
315
330
  it('Doesnt go into pointing_arrow_label mode if not selecting the arrow shape', () => {
316
331
  editor.createShapes<TLArrowShape>([
317
- { id: ids.arrow1, type: 'arrow', x: 100, y: 100, props: { text: 'Test Label' } },
332
+ {
333
+ id: ids.arrow1,
334
+ type: 'arrow',
335
+ x: 100,
336
+ y: 100,
337
+ props: {
338
+ richText: toRichText(''), // Empty label
339
+ start: { x: 0, y: 0 },
340
+ end: { x: 100, y: 0 },
341
+ },
342
+ },
318
343
  ])
319
- const shape = editor.getShape(ids.arrow1)
320
- editor.pointerDown(0, 150, {
344
+ const shape = editor.getShape(ids.arrow1)!
345
+ // Click anywhere on the arrow - since there's no label, it should go to translating
346
+ editor.pointerDown(150, 100, {
321
347
  target: 'shape',
322
348
  shape,
323
349
  })
324
- editor.pointerMove(100, 100)
350
+ editor.pointerMove(155, 105)
325
351
  editor.expectToBeIn('select.translating')
326
352
 
327
353
  editor.pointerUp()
@@ -288,11 +288,13 @@ describe('When shapes are overlapping', () => {
288
288
  editor.pointerDown(0, 50) // over nothing
289
289
  editor.pointerMove(125, 50) // over box1 only
290
290
  expect(bindings().end).toMatchObject({ toId: ids.box1 })
291
- editor.pointerMove(175, 50) // box2 is higher but box1 is filled?
291
+ editor.pointerMove(175, 50) // box2 is higher but box1 is filled, but we're on the edge ofd box 2
292
+ expect(bindings().end).toMatchObject({ toId: ids.box2 })
293
+ editor.pointerMove(175, 70) // box2 is higher but box1 is filled, and we're inside of box2
292
294
  expect(bindings().end).toMatchObject({ toId: ids.box1 })
293
- editor.pointerMove(225, 50) // box3 is higher
295
+ editor.pointerMove(225, 70) // box3 is higher
294
296
  expect(bindings().end).toMatchObject({ toId: ids.box3 })
295
- editor.pointerMove(275, 50) // box4 is higher but box 3 is filled
297
+ editor.pointerMove(275, 70) // box4 is higher but box 3 is filled
296
298
  expect(bindings().end).toMatchObject({ toId: ids.box3 })
297
299
  })
298
300
 
@@ -304,14 +306,18 @@ describe('When shapes are overlapping', () => {
304
306
  ])
305
307
  editor.setCurrentTool('arrow')
306
308
  editor.pointerDown(0, 50)
307
- editor.pointerMove(175, 50) // box1 is smaller even though it's behind box2
309
+ editor.pointerMove(175, 50) // box1 is smaller even though it's behind box2, but we're on the edge of box 2
310
+ expect(bindings().end).toMatchObject({ toId: ids.box2 })
311
+ editor.pointerMove(175, 70) // box1 is smaller even though it's behind box2
308
312
  expect(bindings().end).toMatchObject({ toId: ids.box1 })
309
- editor.pointerMove(150, 90) // box3 is smaller and at the front
313
+ editor.pointerMove(150, 90) // box3 is smaller and at the front but we're on the edge of box 2
314
+ expect(bindings().end).toMatchObject({ toId: ids.box2 })
315
+ editor.pointerMove(160, 90) // box3 is smaller and at the front and we're in box1 and box 3 and box 2
310
316
  expect(bindings().end).toMatchObject({ toId: ids.box3 })
311
317
  editor.sendToBack([ids.box3])
312
318
  editor.pointerMove(149, 90) // box3 is smaller, even when at the back
313
319
  expect(bindings().end).toMatchObject({ toId: ids.box3 })
314
- editor.pointerMove(175, 50)
320
+ editor.pointerMove(175, 60) // inside of box1 and box 2, but box 1 is smaller
315
321
  expect(bindings().end).toMatchObject({ toId: ids.box1 })
316
322
  })
317
323
  })
@@ -1,4 +1,4 @@
1
- import { PageRecordType } from '@tldraw/editor'
1
+ import { PageRecordType, createShapeId } from '@tldraw/editor'
2
2
  import { TestEditor } from '../TestEditor'
3
3
 
4
4
  let editor: TestEditor
@@ -76,4 +76,87 @@ describe('deletePage', () => {
76
76
  expect(editor.getCurrentPageId()).not.toBe(currentPageId)
77
77
  expect(editor.getCurrentPageId()).toBe(editor.getPages()[0].id)
78
78
  })
79
+
80
+ it('deletes all shapes that belong to the deleted page', () => {
81
+ // Create a second page
82
+ const page2Id = PageRecordType.createId('page2')
83
+ editor.createPage({ name: 'Page 2', id: page2Id })
84
+
85
+ // Switch to the second page
86
+ editor.setCurrentPage(page2Id)
87
+
88
+ // Add some shapes to the second page
89
+ const shape1Id = createShapeId('shape1')
90
+ const shape2Id = createShapeId('shape2')
91
+ const shape3Id = createShapeId('shape3')
92
+
93
+ editor.createShape({ id: shape1Id, type: 'text', x: 100, y: 100 })
94
+ editor.createShape({ id: shape2Id, type: 'geo', x: 200, y: 200, props: { geo: 'rectangle' } })
95
+ editor.createShape({ id: shape3Id, type: 'geo', x: 300, y: 300, props: { geo: 'ellipse' } })
96
+
97
+ // Verify shapes were created and belong to the second page
98
+ expect(editor.getShape(shape1Id)).toBeDefined()
99
+ expect(editor.getShape(shape2Id)).toBeDefined()
100
+ expect(editor.getShape(shape3Id)).toBeDefined()
101
+ expect(editor.getShape(shape1Id)?.parentId).toBe(page2Id)
102
+ expect(editor.getShape(shape2Id)?.parentId).toBe(page2Id)
103
+ expect(editor.getShape(shape3Id)?.parentId).toBe(page2Id)
104
+
105
+ // Delete the second page
106
+ editor.deletePage(page2Id)
107
+
108
+ // Verify the page was deleted
109
+ expect(editor.getPages().length).toBe(1)
110
+ expect(editor.getPages()[0].id).not.toBe(page2Id)
111
+
112
+ // Verify all shapes that belonged to the deleted page were also deleted
113
+ expect(editor.getShape(shape1Id)).toBeUndefined()
114
+ expect(editor.getShape(shape2Id)).toBeUndefined()
115
+ expect(editor.getShape(shape3Id)).toBeUndefined()
116
+ })
117
+
118
+ it('deletes locked shapes that belong to the deleted page', () => {
119
+ // Create a second page
120
+ const page2Id = PageRecordType.createId('page2')
121
+ editor.createPage({ name: 'Page 2', id: page2Id })
122
+
123
+ // Switch to the second page
124
+ editor.setCurrentPage(page2Id)
125
+
126
+ // Add some shapes to the second page
127
+ const shape1Id = createShapeId('shape1')
128
+ const shape2Id = createShapeId('shape2')
129
+ const shape3Id = createShapeId('shape3')
130
+
131
+ editor.createShape({ id: shape1Id, type: 'text', x: 100, y: 100 })
132
+ editor.createShape({ id: shape2Id, type: 'geo', x: 200, y: 200, props: { geo: 'rectangle' } })
133
+ editor.createShape({ id: shape3Id, type: 'geo', x: 300, y: 300, props: { geo: 'ellipse' } })
134
+
135
+ // Lock some of the shapes
136
+ editor.updateShape({ id: shape1Id, type: 'text', isLocked: true })
137
+ editor.updateShape({ id: shape2Id, type: 'geo', isLocked: true })
138
+
139
+ // Verify shapes were created and belong to the second page
140
+ expect(editor.getShape(shape1Id)).toBeDefined()
141
+ expect(editor.getShape(shape2Id)).toBeDefined()
142
+ expect(editor.getShape(shape3Id)).toBeDefined()
143
+ expect(editor.getShape(shape1Id)?.parentId).toBe(page2Id)
144
+ expect(editor.getShape(shape2Id)?.parentId).toBe(page2Id)
145
+ expect(editor.getShape(shape3Id)?.parentId).toBe(page2Id)
146
+ expect(editor.getShape(shape1Id)?.isLocked).toBe(true)
147
+ expect(editor.getShape(shape2Id)?.isLocked).toBe(true)
148
+ expect(editor.getShape(shape3Id)?.isLocked).toBe(false)
149
+
150
+ // Delete the second page
151
+ editor.deletePage(page2Id)
152
+
153
+ // Verify the page was deleted
154
+ expect(editor.getPages().length).toBe(1)
155
+ expect(editor.getPages()[0].id).not.toBe(page2Id)
156
+
157
+ // Verify all shapes that belonged to the deleted page were also deleted, including locked ones
158
+ expect(editor.getShape(shape1Id)).toBeUndefined()
159
+ expect(editor.getShape(shape2Id)).toBeUndefined()
160
+ expect(editor.getShape(shape3Id)).toBeUndefined()
161
+ })
79
162
  })
@@ -0,0 +1,315 @@
1
+ import { TLArrowShape, createShapeId } from '@tldraw/editor'
2
+ import { getArrowBindings } from '../lib/shapes/arrow/shared'
3
+ import { TestEditor } from './TestEditor'
4
+
5
+ let editor: TestEditor
6
+
7
+ const ids = {
8
+ solidShape: createShapeId('solidShape'),
9
+ hollowShape: createShapeId('hollowShape'),
10
+ arrow: createShapeId('arrow'),
11
+ }
12
+
13
+ const _arrow = () => editor.getOnlySelectedShape() as TLArrowShape
14
+
15
+ beforeEach(() => {
16
+ editor = new TestEditor()
17
+ })
18
+
19
+ describe('Inner/Outer Margin Shape Detection', () => {
20
+ describe('getShapeAtPoint with inner/outer margins', () => {
21
+ beforeEach(() => {
22
+ // Create a solid filled shape
23
+ editor.createShape({
24
+ id: ids.solidShape,
25
+ type: 'geo',
26
+ x: 100,
27
+ y: 100,
28
+ props: {
29
+ w: 100,
30
+ h: 100,
31
+ fill: 'solid',
32
+ },
33
+ })
34
+
35
+ // Create a hollow shape on top (same position, smaller size)
36
+ editor.createShape({
37
+ id: ids.hollowShape,
38
+ type: 'geo',
39
+ x: 125,
40
+ y: 125,
41
+ props: {
42
+ w: 50,
43
+ h: 50,
44
+ // No fill property - defaults to 'none' (hollow)
45
+ },
46
+ })
47
+ })
48
+
49
+ it('should support inner/outer margin options', () => {
50
+ // Test that the new margin options are accepted
51
+ const point = { x: 150, y: 150 }
52
+
53
+ // Test with array margin [innerMargin, outerMargin]
54
+ const arrayMarginHit = editor.getShapeAtPoint(point, {
55
+ hitInside: true,
56
+ margin: [8, 4],
57
+ })
58
+ expect(arrayMarginHit).toBeDefined()
59
+
60
+ // Test with insideMargin option
61
+ const insideMarginHit = editor.getShapeAtPoint(point, {
62
+ hitInside: true,
63
+ })
64
+ expect(insideMarginHit).toBeDefined()
65
+
66
+ // Test with single number margin (should use same for both)
67
+ const singleMarginHit = editor.getShapeAtPoint(point, {
68
+ hitInside: true,
69
+ margin: 8,
70
+ })
71
+ expect(singleMarginHit).toBeDefined()
72
+ })
73
+
74
+ it('should respect hitInside option for hollow shapes', () => {
75
+ const point = { x: 150, y: 150 }
76
+
77
+ // Without hitInside, should not hit hollow shape
78
+ const noHitInsideHit = editor.getShapeAtPoint(point, {
79
+ margin: [8, 0],
80
+ })
81
+ expect(noHitInsideHit?.id).toBe(ids.solidShape)
82
+
83
+ // With hitInside, should be able to hit hollow shape
84
+ const withHitInsideHit = editor.getShapeAtPoint(point, {
85
+ hitInside: true,
86
+ margin: [8, 0],
87
+ })
88
+ expect(withHitInsideHit).toBeDefined()
89
+ })
90
+
91
+ it('should handle edge cases correctly', () => {
92
+ // Test point exactly on the edge of hollow shape
93
+ const edgePoint = { x: 125, y: 150 }
94
+
95
+ const edgeHit = editor.getShapeAtPoint(edgePoint, {
96
+ hitInside: true,
97
+ margin: [8, 8],
98
+ })
99
+ expect(edgeHit).toBeDefined()
100
+ })
101
+ })
102
+
103
+ describe('Arrow binding with inner/outer margins', () => {
104
+ beforeEach(() => {
105
+ // Create a solid filled shape
106
+ editor.createShape({
107
+ id: ids.solidShape,
108
+ type: 'geo',
109
+ x: 100,
110
+ y: 100,
111
+ props: {
112
+ w: 100,
113
+ h: 100,
114
+ fill: 'solid',
115
+ },
116
+ })
117
+
118
+ // Create a hollow shape on top (same position, smaller size)
119
+ editor.createShape({
120
+ id: ids.hollowShape,
121
+ type: 'geo',
122
+ x: 125,
123
+ y: 125,
124
+ props: {
125
+ w: 50,
126
+ h: 50,
127
+ // No fill property - defaults to 'none' (hollow)
128
+ },
129
+ })
130
+ })
131
+
132
+ it('should create arrow bindings with inner/outer margin support', () => {
133
+ editor.setCurrentTool('arrow')
134
+
135
+ // Start arrow outside both shapes
136
+ editor.pointerDown(50, 150)
137
+
138
+ // Move to center of hollow shape (which overlaps solid shape)
139
+ editor.pointerMove(150, 150)
140
+ editor.pointerUp()
141
+
142
+ const createdArrow = editor
143
+ .getCurrentPageShapes()
144
+ .find((s) => s.type === 'arrow') as TLArrowShape
145
+ expect(createdArrow).toBeDefined()
146
+
147
+ const arrowBindings = getArrowBindings(editor, createdArrow)
148
+ expect(arrowBindings.end).toBeDefined()
149
+ // The binding should be to one of the shapes
150
+ expect([ids.solidShape, ids.hollowShape]).toContain(arrowBindings.end?.toId)
151
+ })
152
+
153
+ it('should bind to solid shape when no hollow shape is present', () => {
154
+ // Remove the hollow shape
155
+ editor.deleteShape(ids.hollowShape)
156
+
157
+ editor.setCurrentTool('arrow')
158
+
159
+ // Start arrow outside shape
160
+ editor.pointerDown(50, 150)
161
+
162
+ // Move to center of solid shape
163
+ editor.pointerMove(150, 150)
164
+ editor.pointerUp()
165
+
166
+ const createdArrow = editor
167
+ .getCurrentPageShapes()
168
+ .find((s) => s.type === 'arrow') as TLArrowShape
169
+ expect(createdArrow).toBeDefined()
170
+
171
+ const arrowBindings = getArrowBindings(editor, createdArrow)
172
+ expect(arrowBindings.end).toBeDefined()
173
+ expect(arrowBindings.end?.toId).toBe(ids.solidShape)
174
+ })
175
+ })
176
+
177
+ describe('Complex overlapping scenarios', () => {
178
+ it('should handle multiple overlapping shapes correctly', () => {
179
+ // Create multiple shapes with different fill states
180
+ editor.createShape({
181
+ id: ids.solidShape,
182
+ type: 'geo',
183
+ x: 100,
184
+ y: 100,
185
+ props: {
186
+ w: 100,
187
+ h: 100,
188
+ fill: 'solid',
189
+ },
190
+ })
191
+
192
+ editor.createShape({
193
+ id: ids.hollowShape,
194
+ type: 'geo',
195
+ x: 125,
196
+ y: 125,
197
+ props: {
198
+ w: 50,
199
+ h: 50,
200
+ // No fill property - defaults to 'none' (hollow)
201
+ },
202
+ })
203
+
204
+ // Create another hollow shape
205
+ const hollowShape2 = createShapeId('hollowShape2')
206
+ editor.createShape({
207
+ id: hollowShape2,
208
+ type: 'geo',
209
+ x: 140,
210
+ y: 140,
211
+ props: {
212
+ w: 30,
213
+ h: 30,
214
+ // No fill property - defaults to 'none' (hollow)
215
+ },
216
+ })
217
+
218
+ // Test point in the innermost hollow shape
219
+ const point = { x: 155, y: 155 }
220
+
221
+ const hit = editor.getShapeAtPoint(point, {
222
+ hitInside: true,
223
+ margin: [8, 8],
224
+ })
225
+ expect(hit).toBeDefined()
226
+ // Should hit one of the shapes
227
+ expect([ids.solidShape, ids.hollowShape, hollowShape2]).toContain(hit?.id)
228
+ })
229
+
230
+ it('should handle shapes with different geometries', () => {
231
+ // Create a solid rectangle
232
+ editor.createShape({
233
+ id: ids.solidShape,
234
+ type: 'geo',
235
+ x: 100,
236
+ y: 100,
237
+ props: {
238
+ w: 100,
239
+ h: 100,
240
+ fill: 'solid',
241
+ },
242
+ })
243
+
244
+ // Create a hollow circle on top
245
+ editor.createShape({
246
+ id: ids.hollowShape,
247
+ type: 'geo',
248
+ x: 125,
249
+ y: 125,
250
+ props: {
251
+ w: 50,
252
+ h: 50,
253
+ geo: 'ellipse',
254
+ // No fill property - defaults to 'none' (hollow)
255
+ },
256
+ })
257
+
258
+ // Test point inside the circle
259
+ const point = { x: 150, y: 150 }
260
+
261
+ const hit = editor.getShapeAtPoint(point, {
262
+ hitInside: true,
263
+ margin: [8, 8],
264
+ })
265
+ expect(hit).toBeDefined()
266
+ // Should hit one of the shapes
267
+ expect([ids.solidShape, ids.hollowShape]).toContain(hit?.id)
268
+ })
269
+ })
270
+
271
+ describe('Regression tests for the original bug', () => {
272
+ it('should support binding to hollow shapes when overlapping solid shapes', () => {
273
+ // This test verifies that the infrastructure exists for the bug fix
274
+ // Create a solid shape
275
+ editor.createShape({
276
+ id: ids.solidShape,
277
+ type: 'geo',
278
+ x: 100,
279
+ y: 100,
280
+ props: {
281
+ w: 100,
282
+ h: 100,
283
+ fill: 'solid',
284
+ },
285
+ })
286
+
287
+ // Create a hollow shape on top
288
+ editor.createShape({
289
+ id: ids.hollowShape,
290
+ type: 'geo',
291
+ x: 125,
292
+ y: 125,
293
+ props: {
294
+ w: 50,
295
+ h: 50,
296
+ // No fill property - defaults to 'none' (hollow)
297
+ },
298
+ })
299
+
300
+ // Test that we can detect both shapes
301
+ const point = { x: 150, y: 150 }
302
+
303
+ // Should be able to hit the solid shape without hitInside
304
+ const solidHit = editor.getShapeAtPoint(point)
305
+ expect(solidHit?.id).toBe(ids.solidShape)
306
+
307
+ // Should be able to hit the hollow shape with hitInside and inner margin
308
+ const hollowHit = editor.getShapeAtPoint(point, {
309
+ hitInside: true,
310
+ margin: [8, 0],
311
+ })
312
+ expect(hollowHit).toBeDefined()
313
+ })
314
+ })
315
+ })