tldraw 3.16.0-next.34fddf633325 → 3.16.0-next.4337ae1ab96d

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 (438) hide show
  1. package/dist-cjs/index.d.ts +324 -105
  2. package/dist-cjs/index.js +40 -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/ArrowShapeUtil.js +3 -3
  11. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +6 -0
  13. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +3 -3
  14. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
  15. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  16. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
  17. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  19. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  20. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +18 -12
  21. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  22. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  23. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  24. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +2 -2
  25. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  26. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js +2 -1
  27. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js.map +2 -2
  28. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +5 -1
  29. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  30. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +6 -3
  31. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +5 -1
  33. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  34. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +4 -4
  35. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  36. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -3
  37. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  38. package/dist-cjs/lib/shapes/shared/ShapeFill.js +4 -4
  39. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  40. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  41. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +0 -2
  42. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  43. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
  44. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  45. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
  46. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  47. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  48. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  49. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  50. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  51. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  52. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  53. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  54. package/dist-cjs/lib/ui/TldrawUi.js +27 -12
  55. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  56. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  57. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  58. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +12 -3
  59. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  60. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  61. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  62. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js +3 -2
  63. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js.map +2 -2
  64. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +6 -6
  65. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  66. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  67. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  68. package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
  69. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  70. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +1 -1
  71. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
  72. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +2 -1
  73. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
  74. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js +3 -2
  75. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js.map +2 -2
  76. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js +2 -2
  77. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js.map +2 -2
  78. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
  79. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  80. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
  81. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  82. package/dist-cjs/lib/ui/components/{primitives/TldrawUiButtonPicker.js → StylePanel/StylePanelButtonPicker.js} +52 -56
  83. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
  84. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
  85. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
  86. package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +26 -25
  87. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
  88. package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +47 -43
  89. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
  90. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
  91. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
  92. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  93. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +39 -10
  95. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  96. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -21
  97. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  98. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
  99. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
  100. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
  101. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  102. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +189 -80
  103. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  104. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +2 -2
  105. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
  106. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +10 -1
  107. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  108. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +3 -2
  109. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +3 -3
  110. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +17 -4
  111. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  112. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +21 -4
  113. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  114. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +161 -159
  115. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  116. package/dist-cjs/lib/ui/components/primitives/layout.js +76 -0
  117. package/dist-cjs/lib/ui/components/primitives/layout.js.map +7 -0
  118. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  119. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  120. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  121. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +154 -20
  122. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  123. package/dist-cjs/lib/ui/context/actions.js +23 -10
  124. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  125. package/dist-cjs/lib/ui/context/components.js +2 -0
  126. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  127. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  128. package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
  129. package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
  130. package/dist-cjs/lib/ui/hooks/useTools.js +94 -9
  131. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  132. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  133. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  134. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  135. package/dist-cjs/lib/ui/kbd-utils.js +9 -3
  136. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  137. package/dist-cjs/lib/ui/version.js +3 -3
  138. package/dist-cjs/lib/ui/version.js.map +1 -1
  139. package/dist-cjs/lib/utils/export/copyAs.js +1 -2
  140. package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
  141. package/dist-cjs/lib/utils/export/export.js +0 -20
  142. package/dist-cjs/lib/utils/export/export.js.map +2 -2
  143. package/dist-cjs/lib/utils/export/exportAs.js +1 -2
  144. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  145. package/dist-esm/index.d.mts +324 -105
  146. package/dist-esm/index.mjs +75 -29
  147. package/dist-esm/index.mjs.map +2 -2
  148. package/dist-esm/lib/Tldraw.mjs +14 -4
  149. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  150. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  151. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  152. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  153. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  154. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -3
  155. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  156. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +6 -0
  157. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +3 -3
  158. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  159. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  160. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
  161. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  162. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  163. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  164. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +19 -12
  165. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  166. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  167. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  168. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
  169. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  170. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
  171. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
  172. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
  173. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  174. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +6 -3
  175. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  176. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +6 -1
  177. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  178. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
  179. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  180. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
  181. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  182. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +5 -4
  183. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  184. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  185. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +0 -2
  186. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  187. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
  188. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  189. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
  190. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  191. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  192. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  193. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  194. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  195. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  196. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  197. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  198. package/dist-esm/lib/ui/TldrawUi.mjs +29 -14
  199. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  200. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  201. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  202. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +12 -3
  203. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  204. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  205. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  206. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs +3 -2
  207. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs.map +2 -2
  208. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +6 -6
  209. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  210. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  211. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  212. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
  213. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  214. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +1 -1
  215. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
  216. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +2 -1
  217. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
  218. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs +3 -2
  219. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs.map +2 -2
  220. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs +2 -2
  221. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs.map +2 -2
  222. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
  223. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  224. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
  225. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  226. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +126 -0
  227. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
  228. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
  229. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
  230. package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +23 -22
  231. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
  232. package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +44 -40
  233. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
  234. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
  235. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
  236. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  237. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  238. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +39 -10
  239. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  240. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -21
  241. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  242. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
  243. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
  244. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  245. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  246. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -81
  247. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  248. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +2 -2
  249. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
  250. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +10 -1
  251. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  252. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +3 -2
  253. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
  254. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +17 -4
  255. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  256. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +21 -4
  257. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  258. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +170 -161
  259. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  260. package/dist-esm/lib/ui/components/primitives/layout.mjs +46 -0
  261. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +7 -0
  262. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  263. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  264. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  265. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +162 -22
  266. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  267. package/dist-esm/lib/ui/context/actions.mjs +23 -10
  268. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  269. package/dist-esm/lib/ui/context/components.mjs +2 -0
  270. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  271. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  272. package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
  273. package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
  274. package/dist-esm/lib/ui/hooks/useTools.mjs +102 -10
  275. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  276. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  277. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  278. package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
  279. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  280. package/dist-esm/lib/ui/version.mjs +3 -3
  281. package/dist-esm/lib/ui/version.mjs.map +1 -1
  282. package/dist-esm/lib/utils/export/copyAs.mjs +1 -2
  283. package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
  284. package/dist-esm/lib/utils/export/export.mjs +0 -20
  285. package/dist-esm/lib/utils/export/export.mjs.map +2 -2
  286. package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
  287. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  288. package/package.json +11 -34
  289. package/src/index.ts +56 -22
  290. package/src/lib/Tldraw.tsx +15 -2
  291. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  292. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  293. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  294. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  295. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
  296. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -3
  297. package/src/lib/shapes/arrow/arrowLabel.ts +8 -0
  298. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  299. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  300. package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
  301. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  302. package/src/lib/shapes/frame/FrameShapeUtil.tsx +29 -14
  303. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  304. package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
  305. package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
  306. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
  307. package/src/lib/shapes/image/ImageShapeUtil.tsx +6 -3
  308. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  309. package/src/lib/shapes/line/LineShapeUtil.tsx +6 -1
  310. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  311. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
  312. package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
  313. package/src/lib/shapes/shared/ShapeFill.tsx +5 -4
  314. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  315. package/src/lib/shapes/shared/useEditablePlainText.ts +0 -6
  316. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
  317. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  318. package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
  319. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  320. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  321. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  322. package/src/lib/tools/SelectTool/childStates/Translating.ts +0 -1
  323. package/src/lib/ui/TldrawUi.tsx +33 -12
  324. package/src/lib/ui/assetUrls.ts +13 -10
  325. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +15 -3
  326. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  327. package/src/lib/ui/components/DefaultMenuPanel.tsx +4 -3
  328. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +6 -6
  329. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  330. package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
  331. package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +1 -1
  332. package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +3 -2
  333. package/src/lib/ui/components/SharePanel/PeopleMenuItem.tsx +4 -3
  334. package/src/lib/ui/components/SharePanel/UserPresenceColorPicker.tsx +3 -3
  335. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
  336. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -385
  337. package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +66 -50
  338. package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
  339. package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +31 -22
  340. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
  341. package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
  342. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  343. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +33 -16
  344. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -23
  345. package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
  346. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
  347. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +212 -61
  348. package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +2 -2
  349. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +6 -1
  350. package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +4 -2
  351. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +50 -30
  352. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +35 -9
  353. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +203 -181
  354. package/src/lib/ui/components/primitives/layout.tsx +107 -0
  355. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  356. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  357. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +221 -19
  358. package/src/lib/ui/context/actions.tsx +23 -10
  359. package/src/lib/ui/context/components.tsx +3 -0
  360. package/src/lib/ui/context/events.tsx +2 -1
  361. package/src/lib/ui/hooks/useExportAs.ts +3 -2
  362. package/src/lib/ui/hooks/useTools.tsx +140 -10
  363. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  364. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  365. package/src/lib/ui/kbd-utils.ts +10 -3
  366. package/src/lib/ui/version.ts +3 -3
  367. package/src/lib/ui.css +381 -306
  368. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  369. package/src/lib/utils/export/copyAs.ts +1 -24
  370. package/src/lib/utils/export/export.ts +0 -36
  371. package/src/lib/utils/export/exportAs.ts +1 -32
  372. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  373. package/src/test/A11y.test.tsx +3 -2
  374. package/src/test/ClickManager.test.ts +7 -6
  375. package/src/test/Editor.test.tsx +20 -19
  376. package/src/test/EraserTool.test.ts +184 -13
  377. package/src/test/HandTool.test.ts +10 -9
  378. package/src/test/HighlightShape.test.ts +2 -1
  379. package/src/test/SelectTool.test.ts +3 -2
  380. package/src/test/TLUserPreferences.test.ts +4 -3
  381. package/src/test/TestEditor.ts +13 -15
  382. package/src/test/TldrawEditor.test.tsx +11 -10
  383. package/src/test/ZoomTool.test.ts +7 -6
  384. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  385. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  386. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  387. package/src/test/arrows-megabus.test.tsx +5 -4
  388. package/src/test/bindings.test.tsx +24 -37
  389. package/src/test/bookmark-shapes.test.ts +1 -8
  390. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  391. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  392. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  393. package/src/test/commands/alignShapes.test.tsx +25 -24
  394. package/src/test/commands/animationSpeed.test.ts +2 -1
  395. package/src/test/commands/centerOnPoint.test.ts +3 -2
  396. package/src/test/commands/clipboard.test.ts +3 -2
  397. package/src/test/commands/createShapes.test.ts +2 -1
  398. package/src/test/commands/deleteShapes.test.ts +2 -1
  399. package/src/test/commands/distributeShapes.test.tsx +11 -10
  400. package/src/test/commands/getSvgString.test.ts +2 -1
  401. package/src/test/commands/packShapes.test.ts +5 -4
  402. package/src/test/commands/resizeShape.test.ts +2 -1
  403. package/src/test/commands/rotateShapes.test.ts +7 -6
  404. package/src/test/commands/setCamera.test.ts +4 -3
  405. package/src/test/commands/setCurrentPage.test.ts +3 -2
  406. package/src/test/commands/stackShapes.test.ts +11 -10
  407. package/src/test/commands/stretch.test.tsx +13 -12
  408. package/src/test/createDeepLink.test.tsx +2 -1
  409. package/src/test/cropping.test.ts +3 -2
  410. package/src/test/custom-clipping.test.ts +436 -0
  411. package/src/test/drawing.test.ts +2 -1
  412. package/src/test/flipShapes.test.ts +4 -3
  413. package/src/test/frames.test.ts +25 -24
  414. package/src/test/getCulledShapes.test.tsx +3 -2
  415. package/src/test/groups.test.tsx +1 -1
  416. package/src/test/handleDeepLink.test.tsx +2 -1
  417. package/src/test/maxShapes.test.ts +3 -2
  418. package/src/test/modifiers.test.ts +5 -4
  419. package/src/test/navigation.test.ts +12 -11
  420. package/src/test/panning.test.ts +2 -1
  421. package/src/test/perf/perf.test.ts +2 -1
  422. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  423. package/src/test/resizing.test.ts +39 -38
  424. package/src/test/select.test.tsx +4 -3
  425. package/src/test/selection-omnibus.test.ts +11 -10
  426. package/src/test/shapeutils.test.ts +4 -3
  427. package/src/test/translating.test.ts +9 -8
  428. package/tldraw.css +682 -596
  429. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  430. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
  431. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
  432. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
  433. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
  434. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
  435. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
  436. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +0 -115
  437. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
  438. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -109
@@ -1,7 +1,9 @@
1
+ import { tltime } from '@tldraw/editor'
1
2
  import { Slider as _Slider } from 'radix-ui'
2
3
  import React, { useCallback, useEffect, useState } from 'react'
3
4
  import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
4
5
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
6
+ import { TldrawUiTooltip, tooltipManager } from './TldrawUiTooltip'
5
7
 
6
8
  /** @public */
7
9
  export interface TLUiSliderProps {
@@ -32,6 +34,7 @@ export const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(
32
34
  ref
33
35
  ) {
34
36
  const msg = useTranslation()
37
+ const [titleAndLabel, setTitleAndLabel] = useState('')
35
38
 
36
39
  // XXX: Radix starts out our slider with a tabIndex of 0
37
40
  // This causes some tab focusing issues, most prevelant in MobileStylePanel,
@@ -49,9 +52,25 @@ export const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(
49
52
  )
50
53
 
51
54
  const handlePointerDown = useCallback(() => {
55
+ tooltipManager.hideAllTooltips()
52
56
  onHistoryMark('click slider')
53
57
  }, [onHistoryMark])
54
58
 
59
+ // N.B. This is a bit silly. The Radix slider auto-focuses which
60
+ // triggers TldrawUiTooltip handleFocus when we dbl-click to edit an image,
61
+ // which in turn makes the tooltip display prematurely.
62
+ // This makes it wait until we've focused to show the tooltip.
63
+ useEffect(() => {
64
+ const timeout = tltime.setTimeout(
65
+ 'set title and label',
66
+ () => {
67
+ setTitleAndLabel(title + ' — ' + msg(label as TLUiTranslationKey))
68
+ },
69
+ 0
70
+ )
71
+ return () => clearTimeout(timeout)
72
+ }, [label, msg, title])
73
+
55
74
  // N.B. Annoying. For a11y purposes, we need Tab to work.
56
75
  // For some reason, Radix has some custom behavior here
57
76
  // that interferes with tabbing past the slider and then
@@ -64,36 +83,37 @@ export const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(
64
83
 
65
84
  return (
66
85
  <div className="tlui-slider__container">
67
- <_Slider.Root
68
- data-testid={testId}
69
- className="tlui-slider"
70
- dir="ltr"
71
- min={min ?? 0}
72
- max={steps}
73
- step={1}
74
- value={value !== null ? [value] : undefined}
75
- onPointerDown={handlePointerDown}
76
- onValueChange={handleValueChange}
77
- onKeyDownCapture={handleKeyEvent}
78
- onKeyUpCapture={handleKeyEvent}
79
- title={title + ' — ' + msg(label as TLUiTranslationKey)}
80
- >
81
- <_Slider.Track className="tlui-slider__track" dir="ltr">
82
- {value !== null && <_Slider.Range className="tlui-slider__range" dir="ltr" />}
83
- </_Slider.Track>
84
- {value !== null && (
85
- <_Slider.Thumb
86
- aria-valuemin={(min ?? 0) * ariaValueModifier}
87
- aria-valuenow={value * ariaValueModifier}
88
- aria-valuemax={steps * ariaValueModifier}
89
- aria-label={title + ' — ' + msg(label as TLUiTranslationKey)}
90
- className="tlui-slider__thumb"
91
- dir="ltr"
92
- ref={ref}
93
- tabIndex={tabIndex}
94
- />
95
- )}
96
- </_Slider.Root>
86
+ <TldrawUiTooltip content={titleAndLabel}>
87
+ <_Slider.Root
88
+ data-testid={testId}
89
+ className="tlui-slider"
90
+ dir="ltr"
91
+ min={min ?? 0}
92
+ max={steps}
93
+ step={1}
94
+ value={value !== null ? [value] : undefined}
95
+ onPointerDown={handlePointerDown}
96
+ onValueChange={handleValueChange}
97
+ onKeyDownCapture={handleKeyEvent}
98
+ onKeyUpCapture={handleKeyEvent}
99
+ >
100
+ <_Slider.Track className="tlui-slider__track" dir="ltr">
101
+ {value !== null && <_Slider.Range className="tlui-slider__range" dir="ltr" />}
102
+ </_Slider.Track>
103
+ {value !== null && (
104
+ <_Slider.Thumb
105
+ aria-valuemin={(min ?? 0) * ariaValueModifier}
106
+ aria-valuenow={value * ariaValueModifier}
107
+ aria-valuemax={steps * ariaValueModifier}
108
+ aria-label={titleAndLabel}
109
+ className="tlui-slider__thumb"
110
+ dir="ltr"
111
+ ref={ref}
112
+ tabIndex={tabIndex}
113
+ />
114
+ )}
115
+ </_Slider.Root>
116
+ </TldrawUiTooltip>
97
117
  </div>
98
118
  )
99
119
  })
@@ -1,6 +1,7 @@
1
1
  import classnames from 'classnames'
2
2
  import { Toolbar as _Toolbar } from 'radix-ui'
3
3
  import React from 'react'
4
+ import { TldrawUiColumn, TldrawUiGrid, TldrawUiRow } from './layout'
4
5
  import { TldrawUiTooltip } from './TldrawUiTooltip'
5
6
 
6
7
  /** @public */
@@ -9,20 +10,42 @@ export interface TLUiToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
9
10
  className?: string
10
11
  dir?: 'ltr' | 'rtl'
11
12
  label: string
13
+ orientation?: 'horizontal' | 'vertical' | 'grid'
14
+ tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
15
+ }
16
+
17
+ const LayoutByOrientation = {
18
+ horizontal: TldrawUiRow,
19
+ vertical: TldrawUiColumn,
20
+ grid: TldrawUiGrid,
12
21
  }
13
22
 
14
23
  /** @public @react */
15
24
  export const TldrawUiToolbar = React.forwardRef<HTMLDivElement, TLUiToolbarProps>(
16
- ({ children, className, label, ...props }: TLUiToolbarProps, ref) => {
25
+ (
26
+ {
27
+ children,
28
+ className,
29
+ label,
30
+ orientation = 'horizontal',
31
+ tooltipSide,
32
+ ...props
33
+ }: TLUiToolbarProps,
34
+ ref
35
+ ) => {
36
+ const Layout = LayoutByOrientation[orientation]
17
37
  return (
18
- <_Toolbar.Root
19
- ref={ref}
20
- {...props}
21
- className={classnames('tlui-toolbar-container', className)}
22
- aria-label={label}
23
- >
24
- {children}
25
- </_Toolbar.Root>
38
+ <Layout asChild tooltipSide={tooltipSide}>
39
+ <_Toolbar.Root
40
+ ref={ref}
41
+ {...props}
42
+ className={classnames('tlui-toolbar', className)}
43
+ aria-label={label}
44
+ orientation={orientation === 'grid' ? 'horizontal' : orientation}
45
+ >
46
+ {children}
47
+ </_Toolbar.Root>
48
+ </Layout>
26
49
  )
27
50
  }
28
51
  )
@@ -71,6 +94,7 @@ export interface TLUiToolbarToggleGroupProps extends React.HTMLAttributes<HTMLDi
71
94
  // TODO: fix up this type later
72
95
  defaultValue?: any
73
96
  type: 'single' | 'multiple'
97
+ asChild?: boolean
74
98
  }
75
99
 
76
100
  /** @public @react */
@@ -78,10 +102,12 @@ export const TldrawUiToolbarToggleGroup = ({
78
102
  children,
79
103
  className,
80
104
  type,
105
+ asChild,
81
106
  ...props
82
107
  }: TLUiToolbarToggleGroupProps) => {
83
108
  return (
84
109
  <_Toolbar.ToggleGroup
110
+ asChild={asChild}
85
111
  type={type}
86
112
  {...props}
87
113
  // TODO: this fixes a bug in Radix until they fix it.
@@ -1,7 +1,15 @@
1
- import { 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,43 +61,61 @@ 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) {
73
- // Only hide if this is the current tooltip
74
- if (this.currentTooltipId === tooltipId) {
75
- // Start destroy timeout (1 second)
76
- if (this.editor) {
77
- this.destroyTimeoutId = this.editor.timers.setTimeout(() => {
78
- this.currentTooltipId = null
79
- this.currentContent = ''
80
- this.activeElement = null
81
- this.destroyTimeoutId = null
82
- this.notify()
83
- }, 300)
75
+ hideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {
76
+ const hide = () => {
77
+ // Only hide if this is the current tooltip
78
+ if (this.currentTooltip.get()?.id === tooltipId) {
79
+ this.currentTooltip.set(null)
80
+ this.destroyTimeoutId = null
84
81
  }
85
82
  }
83
+
84
+ if (editor && !instant) {
85
+ // Start destroy timeout (1 second)
86
+ this.destroyTimeoutId = editor.timers.setTimeout(hide, 300)
87
+ } else {
88
+ hide()
89
+ }
90
+ }
91
+
92
+ hideAllTooltips() {
93
+ this.currentTooltip.set(null)
94
+ this.destroyTimeoutId = null
86
95
  }
87
96
 
88
97
  getCurrentTooltipData() {
89
- return {
90
- id: this.currentTooltipId,
91
- content: this.currentContent,
92
- side: this.currentSide,
93
- sideOffset: this.currentSideOffset,
94
- 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
+ })
95
113
  }
114
+ return this.supportsHoverAtom.get()
96
115
  }
97
116
  }
98
117
 
99
- const tooltipManager = TooltipManager.getInstance()
118
+ export const tooltipManager = TooltipManager.getInstance()
100
119
 
101
120
  // Context for the tooltip singleton
102
121
  const TooltipSingletonContext = createContext<boolean>(false)
@@ -120,66 +139,34 @@ export function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderPro
120
139
 
121
140
  // The singleton tooltip component that renders once
122
141
  function TooltipSingleton() {
123
- const editor = useMaybeEditor()
124
- const [, forceUpdate] = useState({})
125
142
  const [isOpen, setIsOpen] = useState(false)
126
143
  const triggerRef = useRef<HTMLDivElement>(null)
127
- const previousPositionRef = useRef<{ x: number; y: number } | null>(null)
128
- const prefersReducedMotion = usePrefersReducedMotion()
129
- const [shouldAnimate, setShouldAnimate] = useState(false)
130
144
  const isFirstShowRef = useRef(true)
131
- const showTimeoutRef = useRef<number | null>(null)
132
-
133
- // Set editor in tooltip manager
134
- useEffect(() => {
135
- tooltipManager.setEditor(editor)
136
- }, [editor])
145
+ const editor = useMaybeEditor()
137
146
 
138
- // Subscribe to tooltip manager updates
139
- useEffect(() => {
140
- const unsubscribe = tooltipManager.subscribe(() => {
141
- forceUpdate({})
142
- })
143
- return unsubscribe
144
- }, [])
147
+ const currentTooltip = useValue(
148
+ 'current tooltip',
149
+ () => tooltipManager.getCurrentTooltipData(),
150
+ []
151
+ )
145
152
 
146
- const tooltipData = tooltipManager.getCurrentTooltipData()
153
+ const cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])
147
154
 
148
- // Update open state and trigger position
155
+ // Hide tooltip when camera is moving (panning/zooming)
149
156
  useEffect(() => {
150
- const shouldBeOpen = Boolean(tooltipData.id && tooltipData.element)
151
-
152
- // Clear any existing show timeout
153
- if (showTimeoutRef.current) {
154
- clearTimeout(showTimeoutRef.current)
155
- showTimeoutRef.current = null
157
+ if (cameraState === 'moving' && isOpen && currentTooltip) {
158
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true)
156
159
  }
160
+ }, [cameraState, isOpen, currentTooltip, editor])
157
161
 
158
- 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) {
159
166
  // Position the invisible trigger element over the active element
160
- const activeRect = tooltipData.element.getBoundingClientRect()
167
+ const activeRect = currentTooltip.targetElement.getBoundingClientRect()
161
168
  const trigger = triggerRef.current
162
169
 
163
- const newPosition = {
164
- x: activeRect.left + activeRect.width / 2,
165
- y: activeRect.top + activeRect.height / 2,
166
- }
167
-
168
- // Determine if we should animate
169
- let shouldAnimateCheck = false
170
- if (previousPositionRef.current) {
171
- const isNearPrevious = Vec.DistMin(previousPositionRef.current, newPosition, 200)
172
- // Only animate if the distance is less than 200px (nearby tooltips)
173
- shouldAnimateCheck =
174
- !prefersReducedMotion &&
175
- isNearPrevious &&
176
- Math.abs(newPosition.y - previousPositionRef.current.y) < 50
177
- }
178
- // Don't animate on initial show (previousPositionRef.current is null)
179
-
180
- setShouldAnimate(isFirstShowRef.current ? false : shouldAnimateCheck)
181
- previousPositionRef.current = newPosition
182
-
183
170
  trigger.style.position = 'fixed'
184
171
  trigger.style.left = `${activeRect.left}px`
185
172
  trigger.style.top = `${activeRect.top}px`
@@ -189,27 +176,31 @@ function TooltipSingleton() {
189
176
  trigger.style.zIndex = '9999'
190
177
 
191
178
  // Handle delay for first show
192
- if (isFirstShowRef.current && editor) {
193
- showTimeoutRef.current = editor.timers.setTimeout(() => {
179
+ if (isFirstShowRef.current) {
180
+ // eslint-disable-next-line no-restricted-globals
181
+ timer = setTimeout(() => {
194
182
  setIsOpen(true)
195
183
  isFirstShowRef.current = false
196
- }, editor.options.tooltipDelayMs)
184
+ }, currentTooltip.delayDuration)
197
185
  } else {
198
186
  // Subsequent tooltips show immediately
199
187
  setIsOpen(true)
200
188
  }
201
- } else if (!shouldBeOpen) {
189
+ } else {
202
190
  // Hide tooltip immediately
203
191
  setIsOpen(false)
204
- // Reset position tracking when tooltip closes
205
- previousPositionRef.current = null
206
- setShouldAnimate(false)
207
192
  // Reset first show state after tooltip is hidden
208
193
  isFirstShowRef.current = true
209
194
  }
210
- }, [tooltipData.id, tooltipData.element, editor, prefersReducedMotion])
211
195
 
212
- if (!tooltipData.id) {
196
+ return () => {
197
+ if (timer !== null) {
198
+ clearTimeout(timer)
199
+ }
200
+ }
201
+ }, [currentTooltip])
202
+
203
+ if (!currentTooltip) {
213
204
  return null
214
205
  }
215
206
 
@@ -220,14 +211,13 @@ function TooltipSingleton() {
220
211
  </_Tooltip.Trigger>
221
212
  <_Tooltip.Content
222
213
  className="tlui-tooltip"
223
- data-should-animate={shouldAnimate}
224
- side={tooltipData.side}
225
- sideOffset={tooltipData.sideOffset}
214
+ side={currentTooltip.side}
215
+ sideOffset={currentTooltip.sideOffset}
226
216
  avoidCollisions
227
217
  collisionPadding={8}
228
218
  dir="ltr"
229
219
  >
230
- {tooltipData.content}
220
+ {currentTooltip.content}
231
221
  <_Tooltip.Arrow className="tlui-tooltip__arrow" />
232
222
  </_Tooltip.Content>
233
223
  </_Tooltip.Root>
@@ -235,79 +225,111 @@ function TooltipSingleton() {
235
225
  }
236
226
 
237
227
  /** @public @react */
238
- export function TldrawUiTooltip({
239
- children,
240
- content,
241
- side = 'bottom',
242
- sideOffset = 5,
243
- disabled = false,
244
- }: TldrawUiTooltipProps) {
245
- const editor = useMaybeEditor()
246
- const tooltipId = useRef<string>(uniqueId())
247
- 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
+
245
+ const orientationCtx = useTldrawUiOrientation()
246
+ const sideToUse = side ?? orientationCtx.tooltipSide
247
+
248
+ useEffect(() => {
249
+ const currentTooltipId = tooltipId.current
250
+ return () => {
251
+ if (hasProvider) {
252
+ tooltipManager.hideTooltip(editor, currentTooltipId, true)
253
+ }
254
+ }
255
+ }, [editor, hasProvider])
248
256
 
249
- // Don't show tooltip if disabled, no content, or UI labels are disabled
250
- if (disabled || !content) {
251
- return <>{children}</>
252
- }
257
+ // Don't show tooltip if disabled, no content, or UI labels are disabled
258
+ if (disabled || !content) {
259
+ return <>{children}</>
260
+ }
253
261
 
254
- // Fallback to old behavior if no provider
255
- if (!hasProvider) {
256
- return (
257
- <_Tooltip.Root
258
- delayDuration={editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS}
259
- disableHoverableContent
260
- >
261
- <_Tooltip.Trigger asChild>{children}</_Tooltip.Trigger>
262
- <_Tooltip.Content
263
- className="tlui-tooltip"
264
- side={side}
265
- sideOffset={sideOffset}
266
- avoidCollisions
267
- collisionPadding={8}
268
- dir="ltr"
269
- >
270
- {content}
271
- <_Tooltip.Arrow className="tlui-tooltip__arrow" />
272
- </_Tooltip.Content>
273
- </_Tooltip.Root>
274
- )
275
- }
262
+ const delayDurationToUse =
263
+ delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)
264
+
265
+ // Fallback to old behavior if no provider
266
+ if (!hasProvider) {
267
+ return (
268
+ <_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>
269
+ <_Tooltip.Trigger asChild ref={ref}>
270
+ {children}
271
+ </_Tooltip.Trigger>
272
+ <_Tooltip.Content
273
+ className="tlui-tooltip"
274
+ side={sideToUse}
275
+ sideOffset={sideOffset}
276
+ avoidCollisions
277
+ collisionPadding={8}
278
+ dir="ltr"
279
+ >
280
+ {content}
281
+ <_Tooltip.Arrow className="tlui-tooltip__arrow" />
282
+ </_Tooltip.Content>
283
+ </_Tooltip.Root>
284
+ )
285
+ }
276
286
 
277
- const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
278
- tooltipManager.showTooltip(
279
- tooltipId.current,
280
- content,
281
- event.currentTarget as HTMLElement,
282
- side,
283
- sideOffset
284
- )
285
- }
287
+ const child = React.Children.only(children)
288
+ assert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')
289
+
290
+ const handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {
291
+ child.props.onMouseEnter?.(event)
292
+ tooltipManager.showTooltip(
293
+ tooltipId.current,
294
+ content,
295
+ event.currentTarget as HTMLElement,
296
+ sideToUse,
297
+ sideOffset,
298
+ showOnMobile,
299
+ delayDurationToUse
300
+ )
301
+ }
286
302
 
287
- const handleMouseLeave = () => {
288
- tooltipManager.hideTooltip(tooltipId.current)
289
- }
303
+ const handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {
304
+ child.props.onMouseLeave?.(event)
305
+ tooltipManager.hideTooltip(editor, tooltipId.current)
306
+ }
290
307
 
291
- const handleFocus = (event: React.FocusEvent<HTMLElement>) => {
292
- tooltipManager.showTooltip(
293
- tooltipId.current,
294
- content,
295
- event.currentTarget as HTMLElement,
296
- side,
297
- sideOffset
298
- )
299
- }
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
+ sideToUse,
315
+ sideOffset,
316
+ showOnMobile,
317
+ delayDurationToUse
318
+ )
319
+ }
300
320
 
301
- const handleBlur = () => {
302
- tooltipManager.hideTooltip(tooltipId.current)
303
- }
321
+ const handleBlur = (event: React.FocusEvent<HTMLElement>) => {
322
+ child.props.onBlur?.(event)
323
+ tooltipManager.hideTooltip(editor, tooltipId.current)
324
+ }
304
325
 
305
- const childrenWithHandlers = React.cloneElement(children as React.ReactElement, {
306
- onMouseEnter: handleMouseEnter,
307
- onMouseLeave: handleMouseLeave,
308
- onFocus: handleFocus,
309
- onBlur: handleBlur,
310
- })
326
+ const childrenWithHandlers = React.cloneElement(children as React.ReactElement, {
327
+ onMouseEnter: handleMouseEnter,
328
+ onMouseLeave: handleMouseLeave,
329
+ onFocus: handleFocus,
330
+ onBlur: handleBlur,
331
+ })
311
332
 
312
- return childrenWithHandlers
313
- }
333
+ return childrenWithHandlers
334
+ }
335
+ )