tldraw 3.16.0-canary.6f3aedaa1c01 → 3.16.0-canary.783c93150be6

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 (369) hide show
  1. package/dist-cjs/index.d.ts +268 -105
  2. package/dist-cjs/index.js +36 -14
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/Tldraw.js +12 -2
  5. package/dist-cjs/lib/Tldraw.js.map +2 -2
  6. package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
  7. package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
  8. package/dist-cjs/lib/defaultExternalContentHandlers.js +5 -4
  9. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  10. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +6 -0
  11. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +3 -3
  12. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
  13. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  14. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  15. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  16. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +12 -5
  17. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  19. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  20. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -0
  21. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  22. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +6 -3
  23. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +2 -1
  25. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  26. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -3
  27. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  28. package/dist-cjs/lib/shapes/shared/ShapeFill.js +1 -1
  29. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  30. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  31. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +0 -2
  32. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  33. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
  34. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  35. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  36. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  37. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  38. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  39. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  40. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  41. package/dist-cjs/lib/ui/TldrawUi.js +27 -12
  42. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  43. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  44. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  45. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +10 -2
  46. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  47. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  48. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  49. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +6 -6
  50. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  51. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  52. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  53. package/dist-cjs/lib/ui/components/MobileStylePanel.js +4 -2
  54. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  55. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
  56. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  57. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
  58. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  59. package/dist-cjs/lib/ui/components/{primitives/TldrawUiButtonPicker.js → StylePanel/StylePanelButtonPicker.js} +52 -45
  60. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
  61. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
  62. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
  63. package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +23 -22
  64. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
  65. package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +24 -21
  66. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
  67. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
  68. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
  69. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  70. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  71. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +39 -10
  72. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  73. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -22
  74. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  75. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
  76. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
  77. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
  78. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  79. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +188 -78
  80. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  81. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +10 -1
  82. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  83. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +17 -4
  84. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  85. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +17 -3
  86. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  87. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +158 -167
  88. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  89. package/dist-cjs/lib/ui/components/primitives/layout.js +30 -5
  90. package/dist-cjs/lib/ui/components/primitives/layout.js.map +2 -2
  91. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  92. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  93. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +8 -24
  95. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  96. package/dist-cjs/lib/ui/context/actions.js +23 -10
  97. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  98. package/dist-cjs/lib/ui/context/components.js +2 -0
  99. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  100. package/dist-cjs/lib/ui/context/events.js.map +1 -1
  101. package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
  102. package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
  103. package/dist-cjs/lib/ui/hooks/useTools.js +22 -4
  104. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  105. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  106. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +4 -2
  107. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  108. package/dist-cjs/lib/ui/kbd-utils.js +9 -3
  109. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  110. package/dist-cjs/lib/ui/version.js +3 -3
  111. package/dist-cjs/lib/ui/version.js.map +1 -1
  112. package/dist-cjs/lib/utils/export/copyAs.js +1 -2
  113. package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
  114. package/dist-cjs/lib/utils/export/export.js +0 -20
  115. package/dist-cjs/lib/utils/export/export.js.map +2 -2
  116. package/dist-cjs/lib/utils/export/exportAs.js +1 -2
  117. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  118. package/dist-esm/index.d.mts +268 -105
  119. package/dist-esm/index.mjs +68 -30
  120. package/dist-esm/index.mjs.map +2 -2
  121. package/dist-esm/lib/Tldraw.mjs +14 -4
  122. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  123. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  124. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  125. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  126. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  127. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +6 -0
  128. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +3 -3
  129. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  130. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  131. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  132. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  133. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +12 -5
  134. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  135. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  136. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  137. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +1 -0
  138. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  139. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +6 -3
  140. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  141. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +2 -1
  142. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  143. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
  144. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  145. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +1 -1
  146. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  147. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  148. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +0 -2
  149. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  150. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
  151. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  152. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  153. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  154. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  155. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  156. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  157. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  158. package/dist-esm/lib/ui/TldrawUi.mjs +29 -14
  159. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  160. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  161. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  162. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +10 -2
  163. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  164. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  165. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  166. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +6 -6
  167. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  168. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  169. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  170. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +4 -2
  171. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  172. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
  173. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  174. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
  175. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  176. package/dist-esm/lib/ui/components/{primitives/TldrawUiButtonPicker.mjs → StylePanel/StylePanelButtonPicker.mjs} +54 -43
  177. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
  178. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
  179. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
  180. package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +20 -19
  181. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
  182. package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +21 -18
  183. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
  184. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
  185. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
  186. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  187. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  188. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +39 -10
  189. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  190. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -22
  191. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  192. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
  193. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
  194. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  195. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  196. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -80
  197. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  198. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +10 -1
  199. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  200. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +17 -4
  201. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  202. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +18 -4
  203. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  204. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +167 -169
  205. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  206. package/dist-esm/lib/ui/components/primitives/layout.mjs +31 -6
  207. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +2 -2
  208. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  209. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  210. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  211. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +8 -24
  212. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  213. package/dist-esm/lib/ui/context/actions.mjs +23 -10
  214. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  215. package/dist-esm/lib/ui/context/components.mjs +2 -0
  216. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  217. package/dist-esm/lib/ui/context/events.mjs.map +1 -1
  218. package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
  219. package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
  220. package/dist-esm/lib/ui/hooks/useTools.mjs +23 -4
  221. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  222. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +4 -2
  223. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  224. package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
  225. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  226. package/dist-esm/lib/ui/version.mjs +3 -3
  227. package/dist-esm/lib/ui/version.mjs.map +1 -1
  228. package/dist-esm/lib/utils/export/copyAs.mjs +1 -2
  229. package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
  230. package/dist-esm/lib/utils/export/export.mjs +0 -20
  231. package/dist-esm/lib/utils/export/export.mjs.map +2 -2
  232. package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
  233. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  234. package/package.json +11 -34
  235. package/src/index.ts +49 -22
  236. package/src/lib/Tldraw.tsx +15 -2
  237. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  238. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  239. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  240. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  241. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +48 -6
  242. package/src/lib/shapes/arrow/arrowLabel.ts +8 -0
  243. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  244. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  245. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  246. package/src/lib/shapes/frame/FrameShapeUtil.tsx +21 -4
  247. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  248. package/src/lib/shapes/geo/GeoShapeUtil.tsx +1 -0
  249. package/src/lib/shapes/image/ImageShapeUtil.tsx +6 -3
  250. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  251. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  252. package/src/lib/shapes/note/NoteShapeUtil.tsx +1 -0
  253. package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
  254. package/src/lib/shapes/shared/ShapeFill.tsx +1 -1
  255. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  256. package/src/lib/shapes/shared/useEditablePlainText.ts +0 -6
  257. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
  258. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  259. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  260. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  261. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  262. package/src/lib/ui/TldrawUi.tsx +33 -12
  263. package/src/lib/ui/assetUrls.ts +13 -10
  264. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +13 -2
  265. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  266. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +6 -6
  267. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  268. package/src/lib/ui/components/MobileStylePanel.tsx +4 -3
  269. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
  270. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -381
  271. package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +63 -50
  272. package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
  273. package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +28 -19
  274. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
  275. package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
  276. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  277. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +33 -16
  278. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -24
  279. package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
  280. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
  281. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +208 -56
  282. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +6 -1
  283. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +50 -30
  284. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +25 -5
  285. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +197 -188
  286. package/src/lib/ui/components/primitives/layout.tsx +79 -5
  287. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  288. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  289. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +11 -24
  290. package/src/lib/ui/context/actions.tsx +23 -10
  291. package/src/lib/ui/context/components.tsx +3 -0
  292. package/src/lib/ui/context/events.tsx +1 -1
  293. package/src/lib/ui/hooks/useExportAs.ts +3 -2
  294. package/src/lib/ui/hooks/useTools.tsx +26 -4
  295. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  296. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +4 -2
  297. package/src/lib/ui/kbd-utils.ts +10 -3
  298. package/src/lib/ui/version.ts +3 -3
  299. package/src/lib/ui.css +365 -245
  300. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  301. package/src/lib/utils/export/copyAs.ts +1 -24
  302. package/src/lib/utils/export/export.ts +0 -36
  303. package/src/lib/utils/export/exportAs.ts +1 -32
  304. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  305. package/src/test/A11y.test.tsx +3 -2
  306. package/src/test/ClickManager.test.ts +7 -6
  307. package/src/test/Editor.test.tsx +20 -19
  308. package/src/test/EraserTool.test.ts +184 -13
  309. package/src/test/HandTool.test.ts +10 -9
  310. package/src/test/HighlightShape.test.ts +2 -1
  311. package/src/test/SelectTool.test.ts +3 -2
  312. package/src/test/TLUserPreferences.test.ts +4 -3
  313. package/src/test/TestEditor.ts +13 -15
  314. package/src/test/TldrawEditor.test.tsx +11 -10
  315. package/src/test/ZoomTool.test.ts +7 -6
  316. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  317. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  318. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  319. package/src/test/arrows-megabus.test.tsx +5 -4
  320. package/src/test/bindings.test.tsx +24 -37
  321. package/src/test/bookmark-shapes.test.ts +1 -8
  322. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  323. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  324. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  325. package/src/test/commands/alignShapes.test.tsx +25 -24
  326. package/src/test/commands/animationSpeed.test.ts +2 -1
  327. package/src/test/commands/centerOnPoint.test.ts +3 -2
  328. package/src/test/commands/clipboard.test.ts +3 -2
  329. package/src/test/commands/createShapes.test.ts +2 -1
  330. package/src/test/commands/deleteShapes.test.ts +2 -1
  331. package/src/test/commands/distributeShapes.test.tsx +11 -10
  332. package/src/test/commands/getSvgString.test.ts +2 -1
  333. package/src/test/commands/packShapes.test.ts +5 -4
  334. package/src/test/commands/resizeShape.test.ts +2 -1
  335. package/src/test/commands/rotateShapes.test.ts +7 -6
  336. package/src/test/commands/setCamera.test.ts +4 -3
  337. package/src/test/commands/setCurrentPage.test.ts +3 -2
  338. package/src/test/commands/stackShapes.test.ts +11 -10
  339. package/src/test/commands/stretch.test.tsx +13 -12
  340. package/src/test/createDeepLink.test.tsx +2 -1
  341. package/src/test/cropping.test.ts +3 -2
  342. package/src/test/custom-clipping.test.ts +436 -0
  343. package/src/test/drawing.test.ts +2 -1
  344. package/src/test/flipShapes.test.ts +4 -3
  345. package/src/test/frames.test.ts +25 -24
  346. package/src/test/getCulledShapes.test.tsx +74 -4
  347. package/src/test/groups.test.tsx +1 -1
  348. package/src/test/handleDeepLink.test.tsx +2 -1
  349. package/src/test/maxShapes.test.ts +3 -2
  350. package/src/test/modifiers.test.ts +5 -4
  351. package/src/test/navigation.test.ts +12 -11
  352. package/src/test/panning.test.ts +2 -1
  353. package/src/test/perf/perf.test.ts +2 -1
  354. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  355. package/src/test/resizing.test.ts +39 -38
  356. package/src/test/select.test.tsx +4 -3
  357. package/src/test/selection-omnibus.test.ts +11 -10
  358. package/src/test/shapeutils.test.ts +4 -3
  359. package/src/test/translating.test.ts +9 -8
  360. package/tldraw.css +673 -537
  361. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  362. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
  363. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
  364. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
  365. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
  366. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
  367. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
  368. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
  369. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -110
@@ -1,7 +1,15 @@
1
- import { assert, Editor, uniqueId, useMaybeEditor, Vec } from '@tldraw/editor'
1
+ import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'
2
2
  import { Tooltip as _Tooltip } from 'radix-ui'
3
- import React, { createContext, useContext, useEffect, useRef, useState } from 'react'
4
- import { usePrefersReducedMotion } from '../../../shapes/shared/usePrefersReducedMotion'
3
+ import React, {
4
+ createContext,
5
+ forwardRef,
6
+ ReactNode,
7
+ useContext,
8
+ useEffect,
9
+ useRef,
10
+ useState,
11
+ } from 'react'
12
+ import { useTldrawUiOrientation } from './layout'
5
13
 
6
14
  const DEFAULT_TOOLTIP_DELAY_MS = 700
7
15
 
@@ -12,19 +20,23 @@ export interface TldrawUiTooltipProps {
12
20
  side?: 'top' | 'right' | 'bottom' | 'left'
13
21
  sideOffset?: number
14
22
  disabled?: boolean
23
+ showOnMobile?: boolean
24
+ delayDuration?: number
15
25
  }
16
26
 
17
27
  // Singleton tooltip manager
18
28
  class TooltipManager {
19
29
  private static instance: TooltipManager | null = null
20
- private currentTooltipId: string | null = null
21
- private currentContent: string | React.ReactNode = ''
22
- private currentSide: 'top' | 'right' | 'bottom' | 'left' = 'bottom'
23
- private currentSideOffset: number = 5
30
+ private currentTooltip = atom<{
31
+ id: string
32
+ content: ReactNode
33
+ side: 'top' | 'right' | 'bottom' | 'left'
34
+ sideOffset: number
35
+ showOnMobile: boolean
36
+ targetElement: HTMLElement
37
+ delayDuration: number
38
+ } | null>('current tooltip', null)
24
39
  private destroyTimeoutId: number | null = null
25
- private subscribers: Set<() => void> = new Set()
26
- private activeElement: HTMLElement | null = null
27
- private editor: Editor | null = null
28
40
 
29
41
  static getInstance(): TooltipManager {
30
42
  if (!TooltipManager.instance) {
@@ -33,25 +45,14 @@ class TooltipManager {
33
45
  return TooltipManager.instance
34
46
  }
35
47
 
36
- setEditor(editor: Editor | null) {
37
- this.editor = editor
38
- }
39
-
40
- subscribe(callback: () => void): () => void {
41
- this.subscribers.add(callback)
42
- return () => this.subscribers.delete(callback)
43
- }
44
-
45
- private notify() {
46
- this.subscribers.forEach((callback) => callback())
47
- }
48
-
49
48
  showTooltip(
50
49
  tooltipId: string,
51
50
  content: string | React.ReactNode,
52
- element: HTMLElement,
53
- side: 'top' | 'right' | 'bottom' | 'left' = 'bottom',
54
- sideOffset: number = 5
51
+ targetElement: HTMLElement,
52
+ side: 'top' | 'right' | 'bottom' | 'left',
53
+ sideOffset: number,
54
+ showOnMobile: boolean,
55
+ delayDuration: number
55
56
  ) {
56
57
  // Clear any existing destroy timeout
57
58
  if (this.destroyTimeoutId) {
@@ -60,51 +61,57 @@ class TooltipManager {
60
61
  }
61
62
 
62
63
  // Update current tooltip
63
- this.currentTooltipId = tooltipId
64
- this.currentContent = content
65
- this.currentSide = side
66
- this.currentSideOffset = sideOffset
67
- this.activeElement = element
68
-
69
- this.notify()
64
+ this.currentTooltip.set({
65
+ id: tooltipId,
66
+ content,
67
+ side,
68
+ sideOffset,
69
+ showOnMobile,
70
+ targetElement,
71
+ delayDuration,
72
+ })
70
73
  }
71
74
 
72
- hideTooltip(tooltipId: string, instant: boolean = false) {
75
+ hideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {
73
76
  const hide = () => {
74
77
  // Only hide if this is the current tooltip
75
- if (this.currentTooltipId === tooltipId) {
76
- this.currentTooltipId = null
77
- this.currentContent = ''
78
- this.activeElement = null
78
+ if (this.currentTooltip.get()?.id === tooltipId) {
79
+ this.currentTooltip.set(null)
79
80
  this.destroyTimeoutId = null
80
- this.notify()
81
81
  }
82
82
  }
83
83
 
84
- if (instant) {
85
- hide()
86
- } else if (this.editor) {
84
+ if (editor && !instant) {
87
85
  // Start destroy timeout (1 second)
88
- this.destroyTimeoutId = this.editor.timers.setTimeout(hide, 300)
86
+ this.destroyTimeoutId = editor.timers.setTimeout(hide, 300)
87
+ } else {
88
+ hide()
89
89
  }
90
90
  }
91
91
 
92
92
  hideAllTooltips() {
93
- this.currentTooltipId = null
94
- this.currentContent = ''
95
- this.activeElement = null
93
+ this.currentTooltip.set(null)
96
94
  this.destroyTimeoutId = null
97
- this.notify()
98
95
  }
99
96
 
100
97
  getCurrentTooltipData() {
101
- return {
102
- id: this.currentTooltipId,
103
- content: this.currentContent,
104
- side: this.currentSide,
105
- sideOffset: this.currentSideOffset,
106
- element: this.activeElement,
98
+ const currentTooltip = this.currentTooltip.get()
99
+ if (!currentTooltip) return null
100
+ if (!this.supportsHover() && !currentTooltip.showOnMobile) return null
101
+ return currentTooltip
102
+ }
103
+
104
+ private supportsHoverAtom: Atom<boolean> | null = null
105
+ supportsHover() {
106
+ if (!this.supportsHoverAtom) {
107
+ const mediaQuery = window.matchMedia('(hover: hover)')
108
+ const supportsHover = atom('has hover', mediaQuery.matches)
109
+ this.supportsHoverAtom = supportsHover
110
+ mediaQuery.addEventListener('change', (e) => {
111
+ supportsHover.set(e.matches)
112
+ })
107
113
  }
114
+ return this.supportsHoverAtom.get()
108
115
  }
109
116
  }
110
117
 
@@ -132,66 +139,34 @@ export function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderPro
132
139
 
133
140
  // The singleton tooltip component that renders once
134
141
  function TooltipSingleton() {
135
- const editor = useMaybeEditor()
136
- const [, forceUpdate] = useState({})
137
142
  const [isOpen, setIsOpen] = useState(false)
138
143
  const triggerRef = useRef<HTMLDivElement>(null)
139
- const previousPositionRef = useRef<{ x: number; y: number } | null>(null)
140
- const prefersReducedMotion = usePrefersReducedMotion()
141
- const [shouldAnimate, setShouldAnimate] = useState(false)
142
144
  const isFirstShowRef = useRef(true)
143
- const showTimeoutRef = useRef<number | null>(null)
144
-
145
- // Set editor in tooltip manager
146
- useEffect(() => {
147
- tooltipManager.setEditor(editor)
148
- }, [editor])
145
+ const editor = useMaybeEditor()
149
146
 
150
- // Subscribe to tooltip manager updates
151
- useEffect(() => {
152
- const unsubscribe = tooltipManager.subscribe(() => {
153
- forceUpdate({})
154
- })
155
- return unsubscribe
156
- }, [])
147
+ const currentTooltip = useValue(
148
+ 'current tooltip',
149
+ () => tooltipManager.getCurrentTooltipData(),
150
+ []
151
+ )
157
152
 
158
- const tooltipData = tooltipManager.getCurrentTooltipData()
153
+ const cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])
159
154
 
160
- // Update open state and trigger position
155
+ // Hide tooltip when camera is moving (panning/zooming)
161
156
  useEffect(() => {
162
- const shouldBeOpen = Boolean(tooltipData.id && tooltipData.element)
163
-
164
- // Clear any existing show timeout
165
- if (showTimeoutRef.current) {
166
- clearTimeout(showTimeoutRef.current)
167
- showTimeoutRef.current = null
157
+ if (cameraState === 'moving' && isOpen && currentTooltip) {
158
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true)
168
159
  }
160
+ }, [cameraState, isOpen, currentTooltip, editor])
169
161
 
170
- if (shouldBeOpen && tooltipData.element && triggerRef.current) {
162
+ // Update open state and trigger position
163
+ useEffect(() => {
164
+ let timer: ReturnType<typeof setTimeout> | null = null
165
+ if (currentTooltip && triggerRef.current) {
171
166
  // Position the invisible trigger element over the active element
172
- const activeRect = tooltipData.element.getBoundingClientRect()
167
+ const activeRect = currentTooltip.targetElement.getBoundingClientRect()
173
168
  const trigger = triggerRef.current
174
169
 
175
- const newPosition = {
176
- x: activeRect.left + activeRect.width / 2,
177
- y: activeRect.top + activeRect.height / 2,
178
- }
179
-
180
- // Determine if we should animate
181
- let shouldAnimateCheck = false
182
- if (previousPositionRef.current) {
183
- const isNearPrevious = Vec.DistMin(previousPositionRef.current, newPosition, 200)
184
- // Only animate if the distance is less than 200px (nearby tooltips)
185
- shouldAnimateCheck =
186
- !prefersReducedMotion &&
187
- isNearPrevious &&
188
- Math.abs(newPosition.y - previousPositionRef.current.y) < 50
189
- }
190
- // Don't animate on initial show (previousPositionRef.current is null)
191
-
192
- setShouldAnimate(isFirstShowRef.current ? false : shouldAnimateCheck)
193
- previousPositionRef.current = newPosition
194
-
195
170
  trigger.style.position = 'fixed'
196
171
  trigger.style.left = `${activeRect.left}px`
197
172
  trigger.style.top = `${activeRect.top}px`
@@ -201,27 +176,31 @@ function TooltipSingleton() {
201
176
  trigger.style.zIndex = '9999'
202
177
 
203
178
  // Handle delay for first show
204
- if (isFirstShowRef.current && editor) {
205
- showTimeoutRef.current = editor.timers.setTimeout(() => {
179
+ if (isFirstShowRef.current) {
180
+ // eslint-disable-next-line no-restricted-globals
181
+ timer = setTimeout(() => {
206
182
  setIsOpen(true)
207
183
  isFirstShowRef.current = false
208
- }, editor.options.tooltipDelayMs)
184
+ }, currentTooltip.delayDuration)
209
185
  } else {
210
186
  // Subsequent tooltips show immediately
211
187
  setIsOpen(true)
212
188
  }
213
- } else if (!shouldBeOpen) {
189
+ } else {
214
190
  // Hide tooltip immediately
215
191
  setIsOpen(false)
216
- // Reset position tracking when tooltip closes
217
- previousPositionRef.current = null
218
- setShouldAnimate(false)
219
192
  // Reset first show state after tooltip is hidden
220
193
  isFirstShowRef.current = true
221
194
  }
222
- }, [tooltipData.id, tooltipData.element, editor, prefersReducedMotion])
223
195
 
224
- if (!tooltipData.id) {
196
+ return () => {
197
+ if (timer !== null) {
198
+ clearTimeout(timer)
199
+ }
200
+ }
201
+ }, [currentTooltip])
202
+
203
+ if (!currentTooltip) {
225
204
  return null
226
205
  }
227
206
 
@@ -232,14 +211,13 @@ function TooltipSingleton() {
232
211
  </_Tooltip.Trigger>
233
212
  <_Tooltip.Content
234
213
  className="tlui-tooltip"
235
- data-should-animate={shouldAnimate}
236
- side={tooltipData.side}
237
- sideOffset={tooltipData.sideOffset}
214
+ side={currentTooltip.side}
215
+ sideOffset={currentTooltip.sideOffset}
238
216
  avoidCollisions
239
217
  collisionPadding={8}
240
218
  dir="ltr"
241
219
  >
242
- {tooltipData.content}
220
+ {currentTooltip.content}
243
221
  <_Tooltip.Arrow className="tlui-tooltip__arrow" />
244
222
  </_Tooltip.Content>
245
223
  </_Tooltip.Root>
@@ -247,86 +225,117 @@ function TooltipSingleton() {
247
225
  }
248
226
 
249
227
  /** @public @react */
250
- export function TldrawUiTooltip({
251
- children,
252
- content,
253
- side = 'bottom',
254
- sideOffset = 5,
255
- disabled = false,
256
- }: TldrawUiTooltipProps) {
257
- const editor = useMaybeEditor()
258
- const tooltipId = useRef<string>(uniqueId())
259
- const hasProvider = useContext(TooltipSingletonContext)
228
+ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(
229
+ (
230
+ {
231
+ children,
232
+ content,
233
+ side,
234
+ sideOffset = 5,
235
+ disabled = false,
236
+ showOnMobile = false,
237
+ delayDuration,
238
+ },
239
+ ref
240
+ ) => {
241
+ const editor = useMaybeEditor()
242
+ const tooltipId = useRef<string>(uniqueId())
243
+ const hasProvider = useContext(TooltipSingletonContext)
244
+ const showUiLabels = useValue('showUiLabels', () => editor?.user.getShowUiLabels(), [editor])
245
+
246
+ const orientationCtx = useTldrawUiOrientation()
247
+ const sideToUse = side ?? orientationCtx.tooltipSide
248
+
249
+ useEffect(() => {
250
+ const currentTooltipId = tooltipId.current
251
+ return () => {
252
+ if (hasProvider) {
253
+ tooltipManager.hideTooltip(editor, currentTooltipId, true)
254
+ }
255
+ }
256
+ }, [editor, hasProvider])
260
257
 
261
- // Don't show tooltip if disabled, no content, or UI labels are disabled
262
- if (disabled || !content) {
263
- return <>{children}</>
264
- }
258
+ // Don't show tooltip if disabled, no content, or UI labels are disabled
259
+ if (disabled || !content) {
260
+ return <>{children}</>
261
+ }
265
262
 
266
- // Fallback to old behavior if no provider
267
- if (!hasProvider) {
268
- return (
269
- <_Tooltip.Root
270
- delayDuration={editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS}
271
- disableHoverableContent
272
- >
273
- <_Tooltip.Trigger asChild>{children}</_Tooltip.Trigger>
274
- <_Tooltip.Content
275
- className="tlui-tooltip"
276
- side={side}
277
- sideOffset={sideOffset}
278
- avoidCollisions
279
- collisionPadding={8}
280
- dir="ltr"
281
- >
282
- {content}
283
- <_Tooltip.Arrow className="tlui-tooltip__arrow" />
284
- </_Tooltip.Content>
285
- </_Tooltip.Root>
286
- )
287
- }
263
+ let delayDurationToUse
264
+ if (showUiLabels) {
265
+ delayDurationToUse = 0
266
+ } else {
267
+ delayDurationToUse =
268
+ delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)
269
+ }
288
270
 
289
- const child = React.Children.only(children)
290
- assert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')
271
+ // Fallback to old behavior if no provider
272
+ if (!hasProvider) {
273
+ return (
274
+ <_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>
275
+ <_Tooltip.Trigger asChild ref={ref}>
276
+ {children}
277
+ </_Tooltip.Trigger>
278
+ <_Tooltip.Content
279
+ className="tlui-tooltip"
280
+ side={sideToUse}
281
+ sideOffset={sideOffset}
282
+ avoidCollisions
283
+ collisionPadding={8}
284
+ dir="ltr"
285
+ >
286
+ {content}
287
+ <_Tooltip.Arrow className="tlui-tooltip__arrow" />
288
+ </_Tooltip.Content>
289
+ </_Tooltip.Root>
290
+ )
291
+ }
291
292
 
292
- const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
293
- child.props.onMouseEnter?.(event)
294
- tooltipManager.showTooltip(
295
- tooltipId.current,
296
- content,
297
- event.currentTarget as HTMLElement,
298
- side,
299
- sideOffset
300
- )
301
- }
293
+ const child = React.Children.only(children)
294
+ assert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')
295
+
296
+ const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
297
+ child.props.onMouseEnter?.(event)
298
+ tooltipManager.showTooltip(
299
+ tooltipId.current,
300
+ content,
301
+ event.currentTarget as HTMLElement,
302
+ sideToUse,
303
+ sideOffset,
304
+ showOnMobile,
305
+ delayDurationToUse
306
+ )
307
+ }
302
308
 
303
- const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
304
- child.props.onMouseLeave?.(event)
305
- tooltipManager.hideTooltip(tooltipId.current)
306
- }
309
+ const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
310
+ child.props.onMouseLeave?.(event)
311
+ tooltipManager.hideTooltip(editor, tooltipId.current)
312
+ }
307
313
 
308
- const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
309
- child.props.onFocus?.(event)
310
- tooltipManager.showTooltip(
311
- tooltipId.current,
312
- content,
313
- event.currentTarget as HTMLElement,
314
- side,
315
- sideOffset
316
- )
317
- }
314
+ const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
315
+ child.props.onFocus?.(event)
316
+ tooltipManager.showTooltip(
317
+ tooltipId.current,
318
+ content,
319
+ event.currentTarget as HTMLElement,
320
+ sideToUse,
321
+ sideOffset,
322
+ showOnMobile,
323
+ delayDurationToUse
324
+ )
325
+ }
318
326
 
319
- const handleBlur = (event: React.FocusEvent<HTMLElement>) => {
320
- child.props.onBlur?.(event)
321
- tooltipManager.hideTooltip(tooltipId.current)
322
- }
327
+ const handleBlur = (event: React.FocusEvent<HTMLElement>) => {
328
+ child.props.onBlur?.(event)
329
+ tooltipManager.hideTooltip(editor, tooltipId.current)
330
+ }
323
331
 
324
- const childrenWithHandlers = React.cloneElement(children as React.ReactElement, {
325
- onMouseEnter: handleMouseEnter,
326
- onMouseLeave: handleMouseLeave,
327
- onFocus: handleFocus,
328
- onBlur: handleBlur,
329
- })
332
+ const childrenWithHandlers = React.cloneElement(children as React.ReactElement, {
333
+ onMouseEnter: handleMouseEnter,
334
+ onMouseLeave: handleMouseLeave,
335
+ onFocus: handleFocus,
336
+ onBlur: handleBlur,
337
+ })
330
338
 
331
- return childrenWithHandlers
332
- }
339
+ return childrenWithHandlers
340
+ }
341
+ )
@@ -1,10 +1,60 @@
1
1
  import classNames from 'classnames'
2
2
  import { Slot } from 'radix-ui'
3
- import { HTMLAttributes, ReactNode, forwardRef } from 'react'
3
+ import { HTMLAttributes, ReactNode, createContext, forwardRef, useContext } from 'react'
4
+
5
+ /** @public */
6
+ export interface TldrawUiOrientationContext {
7
+ orientation: 'horizontal' | 'vertical'
8
+ tooltipSide: 'top' | 'right' | 'bottom' | 'left'
9
+ }
10
+
11
+ const TldrawUiOrientationContext = createContext<TldrawUiOrientationContext>({
12
+ orientation: 'horizontal',
13
+ tooltipSide: 'bottom',
14
+ })
15
+
16
+ /** @public */
17
+ export interface TldrawUiOrientationProviderProps {
18
+ children: ReactNode
19
+ orientation: 'horizontal' | 'vertical'
20
+ tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
21
+ }
22
+ /** @public @react */
23
+ export function TldrawUiOrientationProvider({
24
+ children,
25
+ orientation,
26
+ tooltipSide,
27
+ }: TldrawUiOrientationProviderProps) {
28
+ const prevContext = useTldrawUiOrientation()
29
+ // generally, we want tooltip side to cascade down through the layout - apart from when the
30
+ // orientation changes. If the tooltip side is "bottom", and then I include some vertical layout
31
+ // elements, keeping the tooltip side as bottom will cause the tooltip to overlap elements
32
+ // stacked on top of each other. In the absence of a tooltip side, we pick a default side based
33
+ // on the orientation whenever the orientation changes.
34
+ const tooltipSideToUse =
35
+ tooltipSide ??
36
+ (orientation === prevContext.orientation
37
+ ? prevContext.tooltipSide
38
+ : orientation === 'horizontal'
39
+ ? 'bottom'
40
+ : 'right')
41
+
42
+ return (
43
+ <TldrawUiOrientationContext.Provider value={{ orientation, tooltipSide: tooltipSideToUse }}>
44
+ {children}
45
+ </TldrawUiOrientationContext.Provider>
46
+ )
47
+ }
48
+
49
+ /** @public */
50
+ export function useTldrawUiOrientation() {
51
+ return useContext(TldrawUiOrientationContext)
52
+ }
4
53
 
5
54
  /** @public */
6
55
  export interface TLUiLayoutProps extends HTMLAttributes<HTMLDivElement> {
7
56
  children: ReactNode
57
+ tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
8
58
  asChild?: boolean
9
59
  }
10
60
 
@@ -14,9 +64,29 @@ export interface TLUiLayoutProps extends HTMLAttributes<HTMLDivElement> {
14
64
  * @public @react
15
65
  */
16
66
  export const TldrawUiRow = forwardRef<HTMLDivElement, TLUiLayoutProps>(
17
- ({ asChild, className, ...props }, ref) => {
67
+ ({ asChild, className, tooltipSide, ...props }, ref) => {
68
+ const Component = asChild ? Slot.Root : 'div'
69
+ return (
70
+ <TldrawUiOrientationProvider orientation="horizontal" tooltipSide={tooltipSide}>
71
+ <Component ref={ref} className={classNames('tlui-row', className)} {...props} />
72
+ </TldrawUiOrientationProvider>
73
+ )
74
+ }
75
+ )
76
+
77
+ /**
78
+ * A column, usually of UI controls like buttons, select dropdown, checkboxes, etc.
79
+ *
80
+ * @public @react
81
+ */
82
+ export const TldrawUiColumn = forwardRef<HTMLDivElement, TLUiLayoutProps>(
83
+ ({ asChild, className, tooltipSide, ...props }, ref) => {
18
84
  const Component = asChild ? Slot.Root : 'div'
19
- return <Component ref={ref} className={classNames('tlui-row', className)} {...props} />
85
+ return (
86
+ <TldrawUiOrientationProvider orientation="vertical" tooltipSide={tooltipSide}>
87
+ <Component ref={ref} className={classNames('tlui-column', className)} {...props} />
88
+ </TldrawUiOrientationProvider>
89
+ )
20
90
  }
21
91
  )
22
92
 
@@ -26,8 +96,12 @@ export const TldrawUiRow = forwardRef<HTMLDivElement, TLUiLayoutProps>(
26
96
  *
27
97
  * @public @react */
28
98
  export const TldrawUiGrid = forwardRef<HTMLDivElement, TLUiLayoutProps>(
29
- ({ asChild, className, ...props }, ref) => {
99
+ ({ asChild, className, tooltipSide, ...props }, ref) => {
30
100
  const Component = asChild ? Slot.Root : 'div'
31
- return <Component ref={ref} className={classNames('tlui-grid', className)} {...props} />
101
+ return (
102
+ <TldrawUiOrientationProvider orientation="horizontal" tooltipSide={tooltipSide}>
103
+ <Component ref={ref} className={classNames('tlui-grid', className)} {...props} />
104
+ </TldrawUiOrientationProvider>
105
+ )
32
106
  }
33
107
  )
@@ -3,7 +3,6 @@ import { TLUiEventSource } from '../../../context/events'
3
3
 
4
4
  /** @public */
5
5
  export type TLUiMenuContextType =
6
- | 'panel'
7
6
  | 'menu'
8
7
  | 'small-icons'
9
8
  | 'context-menu'