tldraw 3.16.0-canary.efdec30fc411 → 3.16.0-canary.f55016ece635

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 (286) hide show
  1. package/dist-cjs/index.d.ts +133 -4
  2. package/dist-cjs/index.js +12 -1
  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/elbow/ElbowArrowDebug.js +3 -3
  13. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  14. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
  15. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  17. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  18. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +12 -12
  19. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  20. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  21. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  22. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +2 -2
  23. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js +2 -1
  25. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js.map +2 -2
  26. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +5 -1
  27. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  28. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
  29. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
  30. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +5 -1
  31. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +4 -4
  33. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  34. package/dist-cjs/lib/shapes/shared/ShapeFill.js +4 -4
  35. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  36. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  37. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
  38. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  39. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  40. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  41. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  42. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  43. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  44. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  45. package/dist-cjs/lib/ui/TldrawUi.js +27 -12
  46. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  47. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  48. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  49. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +10 -2
  50. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  51. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  52. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  53. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +1 -1
  54. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  55. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  56. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  57. package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
  58. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  59. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js +1 -1
  60. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +2 -2
  61. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
  62. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  63. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -22
  64. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  65. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +188 -78
  66. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  67. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js +1 -1
  68. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +2 -2
  69. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +15 -3
  70. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  71. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +146 -168
  72. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  73. package/dist-cjs/lib/ui/components/primitives/layout.js +30 -5
  74. package/dist-cjs/lib/ui/components/primitives/layout.js.map +2 -2
  75. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  76. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  77. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  78. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +3 -19
  79. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  80. package/dist-cjs/lib/ui/context/actions.js +16 -2
  81. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  82. package/dist-cjs/lib/ui/context/components.js +2 -0
  83. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  84. package/dist-cjs/lib/ui/hooks/useTools.js +21 -3
  85. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  86. package/dist-cjs/lib/ui/version.js +3 -3
  87. package/dist-cjs/lib/ui/version.js.map +1 -1
  88. package/dist-esm/index.d.mts +133 -4
  89. package/dist-esm/index.mjs +18 -3
  90. package/dist-esm/index.mjs.map +2 -2
  91. package/dist-esm/lib/Tldraw.mjs +14 -4
  92. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  93. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  94. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  95. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  96. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  97. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -3
  98. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  99. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  100. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  101. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
  102. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  103. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  104. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  105. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +13 -12
  106. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  107. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  108. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  109. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
  110. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  111. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
  112. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
  113. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
  114. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  115. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
  116. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
  117. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +6 -1
  118. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  119. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
  120. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  121. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +5 -4
  122. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  123. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  124. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
  125. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  126. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  127. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  128. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  129. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  130. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  131. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  132. package/dist-esm/lib/ui/TldrawUi.mjs +29 -14
  133. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  134. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  135. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  136. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +10 -2
  137. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  138. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  139. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  140. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +1 -1
  141. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  142. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  143. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  144. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
  145. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  146. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs +1 -1
  147. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +2 -2
  148. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
  149. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  150. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -22
  151. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  152. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -80
  153. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  154. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +2 -1
  155. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +2 -2
  156. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +16 -4
  157. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  158. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +155 -170
  159. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  160. package/dist-esm/lib/ui/components/primitives/layout.mjs +31 -6
  161. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +2 -2
  162. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  163. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  164. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  165. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +3 -19
  166. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  167. package/dist-esm/lib/ui/context/actions.mjs +16 -2
  168. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  169. package/dist-esm/lib/ui/context/components.mjs +2 -0
  170. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  171. package/dist-esm/lib/ui/hooks/useTools.mjs +22 -3
  172. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  173. package/dist-esm/lib/ui/version.mjs +3 -3
  174. package/dist-esm/lib/ui/version.mjs.map +1 -1
  175. package/package.json +11 -34
  176. package/src/index.ts +11 -1
  177. package/src/lib/Tldraw.tsx +15 -2
  178. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  179. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  180. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  181. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  182. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
  183. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -3
  184. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  185. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  186. package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
  187. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  188. package/src/lib/shapes/frame/FrameShapeUtil.tsx +21 -14
  189. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  190. package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
  191. package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
  192. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
  193. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
  194. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  195. package/src/lib/shapes/line/LineShapeUtil.tsx +6 -1
  196. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  197. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
  198. package/src/lib/shapes/shared/ShapeFill.tsx +5 -4
  199. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  200. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  201. package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
  202. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  203. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  204. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  205. package/src/lib/ui/TldrawUi.tsx +33 -12
  206. package/src/lib/ui/assetUrls.ts +13 -10
  207. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +13 -2
  208. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  209. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +1 -1
  210. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  211. package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
  212. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +1 -1
  213. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
  214. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -24
  215. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +208 -56
  216. package/src/lib/ui/components/primitives/TldrawUiButtonPicker.tsx +3 -2
  217. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +22 -5
  218. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +184 -191
  219. package/src/lib/ui/components/primitives/layout.tsx +79 -5
  220. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  221. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  222. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +5 -18
  223. package/src/lib/ui/context/actions.tsx +16 -2
  224. package/src/lib/ui/context/components.tsx +3 -0
  225. package/src/lib/ui/hooks/useTools.tsx +25 -3
  226. package/src/lib/ui/version.ts +3 -3
  227. package/src/lib/ui.css +349 -243
  228. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  229. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  230. package/src/test/A11y.test.tsx +3 -2
  231. package/src/test/ClickManager.test.ts +7 -6
  232. package/src/test/Editor.test.tsx +20 -19
  233. package/src/test/EraserTool.test.ts +184 -13
  234. package/src/test/HandTool.test.ts +10 -9
  235. package/src/test/HighlightShape.test.ts +2 -1
  236. package/src/test/SelectTool.test.ts +3 -2
  237. package/src/test/TLUserPreferences.test.ts +4 -3
  238. package/src/test/TestEditor.ts +13 -15
  239. package/src/test/TldrawEditor.test.tsx +11 -10
  240. package/src/test/ZoomTool.test.ts +7 -6
  241. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  242. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  243. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  244. package/src/test/arrows-megabus.test.tsx +5 -4
  245. package/src/test/bindings.test.tsx +24 -37
  246. package/src/test/bookmark-shapes.test.ts +1 -8
  247. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  248. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  249. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  250. package/src/test/commands/alignShapes.test.tsx +25 -24
  251. package/src/test/commands/animationSpeed.test.ts +2 -1
  252. package/src/test/commands/centerOnPoint.test.ts +3 -2
  253. package/src/test/commands/clipboard.test.ts +3 -2
  254. package/src/test/commands/createShapes.test.ts +2 -1
  255. package/src/test/commands/deleteShapes.test.ts +2 -1
  256. package/src/test/commands/distributeShapes.test.tsx +11 -10
  257. package/src/test/commands/getSvgString.test.ts +2 -1
  258. package/src/test/commands/packShapes.test.ts +5 -4
  259. package/src/test/commands/resizeShape.test.ts +2 -1
  260. package/src/test/commands/rotateShapes.test.ts +7 -6
  261. package/src/test/commands/setCamera.test.ts +4 -3
  262. package/src/test/commands/setCurrentPage.test.ts +3 -2
  263. package/src/test/commands/stackShapes.test.ts +11 -10
  264. package/src/test/commands/stretch.test.tsx +13 -12
  265. package/src/test/createDeepLink.test.tsx +2 -1
  266. package/src/test/cropping.test.ts +3 -2
  267. package/src/test/drawing.test.ts +2 -1
  268. package/src/test/flipShapes.test.ts +4 -3
  269. package/src/test/frames.test.ts +25 -24
  270. package/src/test/getCulledShapes.test.tsx +3 -2
  271. package/src/test/groups.test.tsx +1 -1
  272. package/src/test/handleDeepLink.test.tsx +2 -1
  273. package/src/test/maxShapes.test.ts +3 -2
  274. package/src/test/modifiers.test.ts +5 -4
  275. package/src/test/navigation.test.ts +12 -11
  276. package/src/test/panning.test.ts +2 -1
  277. package/src/test/perf/perf.test.ts +2 -1
  278. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  279. package/src/test/resizing.test.ts +39 -38
  280. package/src/test/select.test.tsx +4 -3
  281. package/src/test/selection-omnibus.test.ts +11 -10
  282. package/src/test/shapeutils.test.ts +4 -3
  283. package/src/test/translating.test.ts +9 -8
  284. package/tldraw.css +650 -533
  285. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  286. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DefaultColorStyle,
3
3
  TLDefaultColorStyle,
4
+ getColorValue,
4
5
  getDefaultColorTheme,
5
6
  useEditor,
6
7
  useValue,
@@ -16,18 +17,20 @@ import {
16
17
  TldrawUiPopoverContent,
17
18
  TldrawUiPopoverTrigger,
18
19
  } from './primitives/TldrawUiPopover'
20
+ import { useTldrawUiOrientation } from './primitives/layout'
19
21
 
20
22
  /** @public @react */
21
23
  export function MobileStylePanel() {
22
24
  const editor = useEditor()
23
25
  const msg = useTranslation()
24
-
26
+ const { orientation } = useTldrawUiOrientation()
25
27
  const relevantStyles = useRelevantStyles()
26
28
  const color = relevantStyles?.get(DefaultColorStyle)
27
29
  const theme = getDefaultColorTheme({ isDarkMode: editor.user.getIsDarkMode() })
28
- const currentColor = (
29
- color?.type === 'shared' ? theme[color.value as TLDefaultColorStyle] : theme.black
30
- ).solid
30
+ const currentColor =
31
+ color?.type === 'shared'
32
+ ? getColorValue(theme, color.value as TLDefaultColorStyle, 'solid')
33
+ : getColorValue(theme, 'black', 'solid')
31
34
 
32
35
  const disableStylePanel = useValue(
33
36
  'disable style panel',
@@ -54,7 +57,7 @@ export function MobileStylePanel() {
54
57
  type="tool"
55
58
  data-testid="mobile-styles.button"
56
59
  style={{
57
- color: disableStylePanel ? 'var(--color-muted-1)' : currentColor,
60
+ color: disableStylePanel ? 'var(--tl-color-muted-1)' : currentColor,
58
61
  }}
59
62
  title={msg('style-panel.title')}
60
63
  disabled={disableStylePanel}
@@ -64,7 +67,7 @@ export function MobileStylePanel() {
64
67
  />
65
68
  </TldrawUiButton>
66
69
  </TldrawUiPopoverTrigger>
67
- <TldrawUiPopoverContent side="top" align="end">
70
+ <TldrawUiPopoverContent side={orientation === 'horizontal' ? 'top' : 'right'} align="end">
68
71
  {StylePanel && <StylePanel isMobile />}
69
72
  </TldrawUiPopoverContent>
70
73
  </TldrawUiPopover>
@@ -74,7 +74,7 @@ function DropdownPickerInner<T extends string>({
74
74
  </TldrawUiToolbarButton>
75
75
  </TldrawUiPopoverTrigger>
76
76
  <TldrawUiPopoverContent side="left" align="center">
77
- <TldrawUiToolbar orientation="grid" label={labelStr}>
77
+ <TldrawUiToolbar orientation={items.length > 4 ? 'grid' : 'horizontal'} label={labelStr}>
78
78
  <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
79
79
  {items.map((item) => {
80
80
  return (
@@ -272,7 +272,7 @@ export const DefaultImageToolbarContent = track(function DefaultImageToolbarCont
272
272
  type="icon"
273
273
  onClick={onManipulatingEnd}
274
274
  data-testid="tool.image-confirm"
275
- style={{ borderLeft: '1px solid var(--color-divider)', marginLeft: '2px' }}
275
+ style={{ borderLeft: '1px solid var(--tl-color-divider)', marginLeft: '2px' }}
276
276
  >
277
277
  <TldrawUiButtonIcon small icon="check" />
278
278
  </TldrawUiButton>
@@ -1,4 +1,5 @@
1
1
  import { useEditor, usePassThroughWheelEvents, useValue } from '@tldraw/editor'
2
+ import classNames from 'classnames'
2
3
  import { ReactNode, memo, useRef } from 'react'
3
4
  import { PORTRAIT_BREAKPOINT } from '../../constants'
4
5
  import { useBreakpoint } from '../../context/breakpoints'
@@ -6,6 +7,7 @@ import { useTldrawUiComponents } from '../../context/components'
6
7
  import { useReadonly } from '../../hooks/useReadonly'
7
8
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
8
9
  import { MobileStylePanel } from '../MobileStylePanel'
10
+ import { TldrawUiOrientationProvider } from '../primitives/layout'
9
11
  import { TldrawUiToolbar } from '../primitives/TldrawUiToolbar'
10
12
  import { DefaultToolbarContent } from './DefaultToolbarContent'
11
13
  import { OverflowingToolbar } from './OverflowingToolbar'
@@ -14,6 +16,11 @@ import { ToggleToolLockedButton } from './ToggleToolLockedButton'
14
16
  /** @public */
15
17
  export interface DefaultToolbarProps {
16
18
  children?: ReactNode
19
+ orientation?: 'horizontal' | 'vertical'
20
+ minItems?: number
21
+ minSizePx?: number
22
+ maxItems?: number
23
+ maxSizePx?: number
17
24
  }
18
25
 
19
26
  /**
@@ -24,7 +31,14 @@ export interface DefaultToolbarProps {
24
31
  * @public
25
32
  * @react
26
33
  */
27
- export const DefaultToolbar = memo(function DefaultToolbar({ children }: DefaultToolbarProps) {
34
+ export const DefaultToolbar = memo(function DefaultToolbar({
35
+ children,
36
+ orientation = 'horizontal',
37
+ minItems = 4,
38
+ minSizePx = 310,
39
+ maxItems = 8,
40
+ maxSizePx = 470,
41
+ }: DefaultToolbarProps) {
28
42
  const editor = useEditor()
29
43
  const msg = useTranslation()
30
44
  const breakpoint = useBreakpoint()
@@ -44,32 +58,49 @@ export const DefaultToolbar = memo(function DefaultToolbar({ children }: Default
44
58
  : breakpoint < PORTRAIT_BREAKPOINT.TABLET
45
59
 
46
60
  return (
47
- <div ref={ref} className="tlui-main-toolbar">
48
- <div className="tlui-main-toolbar__inner">
49
- <div className="tlui-main-toolbar__left">
50
- {!isReadonlyMode && (
51
- <div className="tlui-main-toolbar__extras">
52
- {showQuickActions && (
53
- <TldrawUiToolbar
54
- orientation="horizontal"
55
- className="tlui-main-toolbar__extras__controls"
56
- label={msg('actions-menu.title')}
57
- >
58
- {QuickActions && <QuickActions />}
59
- {ActionsMenu && <ActionsMenu />}
60
- </TldrawUiToolbar>
61
- )}
62
- <ToggleToolLockedButton activeToolId={activeToolId} />
61
+ <TldrawUiOrientationProvider
62
+ orientation={orientation}
63
+ tooltipSide={orientation === 'horizontal' ? 'top' : 'right'}
64
+ >
65
+ <div
66
+ ref={ref}
67
+ className={classNames('tlui-main-toolbar', `tlui-main-toolbar--${orientation}`)}
68
+ >
69
+ <div className="tlui-main-toolbar__inner">
70
+ <div className="tlui-main-toolbar__left">
71
+ {!isReadonlyMode && (
72
+ <div className="tlui-main-toolbar__extras">
73
+ {showQuickActions && (
74
+ <TldrawUiToolbar
75
+ orientation={orientation}
76
+ className="tlui-main-toolbar__extras__controls"
77
+ label={msg('actions-menu.title')}
78
+ >
79
+ {QuickActions && <QuickActions />}
80
+ {ActionsMenu && <ActionsMenu />}
81
+ </TldrawUiToolbar>
82
+ )}
83
+ <ToggleToolLockedButton activeToolId={activeToolId} />
84
+ </div>
85
+ )}
86
+ <OverflowingToolbar
87
+ orientation={orientation}
88
+ sizingParentClassName="tlui-main-toolbar"
89
+ minItems={minItems}
90
+ maxItems={maxItems}
91
+ minSizePx={minSizePx}
92
+ maxSizePx={maxSizePx}
93
+ >
94
+ {children ?? <DefaultToolbarContent />}
95
+ </OverflowingToolbar>
96
+ </div>
97
+ {breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
98
+ <div className="tlui-main-toolbar__tools tlui-main-toolbar__mobile-style-panel">
99
+ <MobileStylePanel />
63
100
  </div>
64
101
  )}
65
- <OverflowingToolbar>{children ?? <DefaultToolbarContent />}</OverflowingToolbar>
66
102
  </div>
67
- {breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
68
- <div className="tlui-main-toolbar__tools">
69
- <MobileStylePanel />
70
- </div>
71
- )}
72
103
  </div>
73
- </div>
104
+ </TldrawUiOrientationProvider>
74
105
  )
75
106
  })
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  activeElementShouldCaptureKeys,
3
+ assert,
4
+ modulate,
3
5
  preventDefault,
4
6
  tlmenus,
5
7
  useEditor,
@@ -7,7 +9,7 @@ import {
7
9
  useUniqueSafeId,
8
10
  } from '@tldraw/editor'
9
11
  import classNames from 'classnames'
10
- import { createContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
12
+ import { createContext, useEffect, useLayoutEffect, useRef, useState } from 'react'
11
13
  import { PORTRAIT_BREAKPOINT } from '../../constants'
12
14
  import { useBreakpoint } from '../../context/breakpoints'
13
15
  import { areShortcutsDisabled } from '../../hooks/useKeyboardShortcuts'
@@ -20,7 +22,7 @@ import {
20
22
  TldrawUiPopoverTrigger,
21
23
  } from '../primitives/TldrawUiPopover'
22
24
  import { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
23
- import { TldrawUiRow } from '../primitives/layout'
25
+ import { TldrawUiColumn, TldrawUiRow } from '../primitives/layout'
24
26
  import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
25
27
 
26
28
  export const IsInOverflowContext = createContext(false)
@@ -41,10 +43,24 @@ const NUMBERED_SHORTCUT_KEYS: Record<string, number> = {
41
43
  /** @public */
42
44
  export interface OverflowingToolbarProps {
43
45
  children: React.ReactNode
46
+ orientation: 'horizontal' | 'vertical'
47
+ sizingParentClassName: string
48
+ minItems: number
49
+ minSizePx: number
50
+ maxItems: number
51
+ maxSizePx: number
44
52
  }
45
53
 
46
54
  /** @public @react */
47
- export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
55
+ export function OverflowingToolbar({
56
+ children,
57
+ orientation,
58
+ sizingParentClassName,
59
+ minItems,
60
+ minSizePx,
61
+ maxItems,
62
+ maxSizePx,
63
+ }: OverflowingToolbarProps) {
48
64
  const editor = useEditor()
49
65
  const id = useUniqueSafeId()
50
66
  const breakpoint = useBreakpoint()
@@ -52,63 +68,165 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
52
68
  const rButtons = useRef<HTMLElement[]>([])
53
69
  const [isOpen, setIsOpen] = useState(false)
54
70
 
55
- const overflowIndex = Math.min(8, 5 + breakpoint)
56
-
57
- const [totalItems, setTotalItems] = useState(0)
58
71
  const mainToolsRef = useRef<HTMLDivElement>(null)
59
- const [lastActiveOverflowItem, setLastActiveOverflowItem] = useState<string | null>(null)
60
72
 
61
- const css = useMemo(() => {
62
- const activeCss = lastActiveOverflowItem ? `:not([data-value="${lastActiveOverflowItem}"])` : ''
63
-
64
- return `
65
- #${id}_main > *:nth-of-type(n + ${overflowIndex + (lastActiveOverflowItem ? 1 : 2)}):not([data-radix-popper-content-wrapper])${activeCss} {
66
- display: none;
67
- }
68
- #${id}_more > *:nth-of-type(-n + ${overflowIndex}):not([data-radix-popper-content-wrapper]) {
69
- display: none;
70
- }
71
- `
72
- }, [lastActiveOverflowItem, id, overflowIndex])
73
+ // we have to use state instead of a ref here so that we get
74
+ // an update when the overflow popover mounts / unmounts
75
+ const [overflowTools, setOverflowTools] = useState<HTMLDivElement | null>(null)
76
+ const [lastActiveOverflowItem, setLastActiveOverflowItem] = useState<string | null>(null)
77
+ const [shouldShowOverflow, setShouldShowOverflow] = useState(false)
73
78
 
74
79
  const onDomUpdate = useEvent(() => {
75
80
  if (!mainToolsRef.current) return
76
81
 
77
- const children = Array.from(mainToolsRef.current.children)
78
- setTotalItems(children.length)
82
+ // whenever we get an update, we need to re-calculate the number of items to show and update
83
+ // the component accordingly.
84
+ const sizeProp = orientation === 'horizontal' ? 'offsetWidth' : 'offsetHeight'
79
85
 
80
- // If the last active overflow item is no longer in the overflow, clear it
81
- const lastActiveElementIdx = children.findIndex(
82
- (el) => el.getAttribute('data-value') === lastActiveOverflowItem
83
- )
84
- if (lastActiveElementIdx <= overflowIndex) {
85
- setLastActiveOverflowItem(null)
86
+ // toolbars can contain both single items and groups. we need to keep track of both.
87
+ type Items = (
88
+ | { type: 'item'; element: HTMLElement }
89
+ | { type: 'group'; items: Items; element: HTMLElement }
90
+ )[]
91
+
92
+ // walk through the dom and collect items so we can calculate what to show/hide
93
+ const mainItems = collectItems(mainToolsRef.current.children)
94
+ const overflowItems = overflowTools ? collectItems(overflowTools.children) : null
95
+ function collectItems(collection: HTMLCollection) {
96
+ const items: Items = []
97
+ for (const child of collection) {
98
+ if (child.classList.contains('tlui-main-toolbar__group')) {
99
+ items.push({
100
+ type: 'group',
101
+ items: collectItems(child.children),
102
+ element: child as HTMLElement,
103
+ })
104
+ } else {
105
+ items.push({ type: 'item', element: child as HTMLElement })
106
+ }
107
+ }
108
+
109
+ return items
86
110
  }
87
111
 
88
- // But if there's a new active item...
89
- const activeElementIdx = Array.from(mainToolsRef.current.children).findIndex(
90
- (el) => el.getAttribute('aria-pressed') === 'true'
112
+ // the number of items to show is based on the space available to the toolbar.
113
+ const sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)
114
+ const size = sizingParent[sizeProp]
115
+ const itemsToShow = Math.floor(
116
+ modulate(size, [minSizePx, maxSizePx], [minItems, maxItems], true)
91
117
  )
92
- if (activeElementIdx === -1) return
93
118
 
94
- // ...and it's in the overflow, set it as the last active overflow item
95
- if (activeElementIdx >= overflowIndex) {
96
- setLastActiveOverflowItem(children[activeElementIdx].getAttribute('data-value'))
97
- }
119
+ // now we know how many items to show, we need to walk through the items we found and show /
120
+ // hide them accordingly. We need to keep track of:
121
+ // the number of item's we've shown in the main content so far
122
+ let mainItemCount = 0
123
+ // the item that is currently active in the overflow content (if any)
124
+ let newActiveOverflowItem: string | null = null
125
+ // whether the last active overflow item is actually still in the overflow content
126
+ let shouldInvalidateLastActiveOverflowItem = false
127
+ // the buttons visible in the main content
128
+ const numberedButtons: HTMLButtonElement[] = []
129
+ function visitItems(
130
+ mainItems: Items,
131
+ overflowItems: Items | null
132
+ ): {
133
+ // for each group of items we visit, we need to know whether we showed anything in
134
+ // either section
135
+ didShowAnyInMain: boolean
136
+ didShowAnyInOverflow: boolean
137
+ } {
138
+ if (overflowItems) assert(mainItems.length === overflowItems.length)
139
+
140
+ let didShowAnyInMain = false
141
+ let didShowAnyInOverflow = false
142
+
143
+ for (let i = 0; i < mainItems.length; i++) {
144
+ const mainItem = mainItems[i]
145
+ const overflowItem = overflowItems?.[i]
146
+
147
+ if (mainItem.type === 'item') {
148
+ const isLastActiveOverflowItem =
149
+ mainItem.element.getAttribute('data-value') === lastActiveOverflowItem
150
+
151
+ // for single items, we show them in main if we have space, or if they're the
152
+ // last-used item from the overflow.
153
+ let shouldShowInMain
154
+ if (lastActiveOverflowItem) {
155
+ shouldShowInMain = mainItemCount < itemsToShow || isLastActiveOverflowItem
156
+ } else {
157
+ // we use <= here because if there is no last active overflow item, we want
158
+ // to show at least one item in the main toolbar.
159
+ shouldShowInMain = mainItemCount <= itemsToShow
160
+ }
161
+ const shouldShowInOverflow = mainItemCount >= itemsToShow
98
162
 
99
- // Save the buttons that are actually visible
100
- rButtons.current = Array.from(mainToolsRef.current?.children ?? []).filter(
101
- (el): el is HTMLElement => {
102
- // only count html elements...
103
- if (!(el instanceof HTMLElement)) return false
163
+ didShowAnyInMain ||= shouldShowInMain
164
+ didShowAnyInOverflow ||= shouldShowInOverflow
104
165
 
105
- // ...that are buttons...
106
- if (el.tagName.toLowerCase() !== 'button') return false
166
+ setAttribute(
167
+ mainItem.element,
168
+ 'data-toolbar-visible',
169
+ shouldShowInMain ? 'true' : 'false'
170
+ )
171
+ if (overflowItem) {
172
+ assert(overflowItem.type === 'item')
173
+ setAttribute(
174
+ overflowItem.element,
175
+ 'data-toolbar-visible',
176
+ shouldShowInOverflow ? 'true' : 'false'
177
+ )
178
+ }
179
+ if (shouldShowInOverflow && mainItem.element.getAttribute('aria-pressed') === 'true') {
180
+ newActiveOverflowItem = mainItem.element.getAttribute('data-value')
181
+ }
182
+ if (shouldShowInMain && mainItem.element.tagName === 'BUTTON') {
183
+ numberedButtons.push(mainItem.element as HTMLButtonElement)
184
+ }
185
+ if (!shouldShowInOverflow && isLastActiveOverflowItem) {
186
+ shouldInvalidateLastActiveOverflowItem = true
187
+ }
188
+ mainItemCount++
189
+ } else {
190
+ // for groups, we show them in main if we have space, or if they're the
191
+ // last-used item from the overflow.
192
+ let result, overflowGroup
193
+ if (overflowItem) {
194
+ assert(overflowItem.type === 'group')
195
+ overflowGroup = overflowItem
196
+ result = visitItems(mainItem.items, overflowGroup.items)
197
+ } else {
198
+ result = visitItems(mainItem.items, null)
199
+ }
107
200
 
108
- // ...that are actually visible
109
- return !!(el.offsetWidth || el.offsetHeight)
201
+ didShowAnyInMain ||= result.didShowAnyInMain
202
+ didShowAnyInOverflow ||= result.didShowAnyInOverflow
203
+
204
+ setAttribute(
205
+ mainItem.element,
206
+ 'data-toolbar-visible',
207
+ result.didShowAnyInMain ? 'true' : 'false'
208
+ )
209
+ if (overflowGroup) {
210
+ setAttribute(
211
+ overflowGroup.element,
212
+ 'data-toolbar-visible',
213
+ result.didShowAnyInOverflow ? 'true' : 'false'
214
+ )
215
+ }
216
+ }
110
217
  }
111
- )
218
+ return { didShowAnyInMain, didShowAnyInOverflow }
219
+ }
220
+
221
+ const { didShowAnyInOverflow } = visitItems(mainItems, overflowItems)
222
+ setShouldShowOverflow(didShowAnyInOverflow)
223
+ if (newActiveOverflowItem) {
224
+ setLastActiveOverflowItem(newActiveOverflowItem)
225
+ } else if (shouldInvalidateLastActiveOverflowItem) {
226
+ setLastActiveOverflowItem(null)
227
+ }
228
+
229
+ rButtons.current = numberedButtons
112
230
  })
113
231
 
114
232
  useLayoutEffect(() => {
@@ -122,20 +240,31 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
122
240
  mutationObserver.observe(mainToolsRef.current, {
123
241
  childList: true,
124
242
  subtree: true,
125
- attributeFilter: ['data-value', 'aria-pressed'],
243
+ attributes: true,
244
+ characterData: true,
126
245
  })
127
246
 
247
+ const sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)
248
+ const resizeObserver = new ResizeObserver(onDomUpdate)
249
+ resizeObserver.observe(sizingParent)
250
+
128
251
  return () => {
129
252
  mutationObserver.disconnect()
253
+ resizeObserver.disconnect()
130
254
  }
131
- }, [onDomUpdate])
255
+ }, [onDomUpdate, sizingParentClassName])
132
256
 
133
257
  useEffect(() => {
134
258
  if (!editor.options.enableToolbarKeyboardShortcuts) return
135
259
 
136
260
  function handleKeyDown(event: KeyboardEvent) {
137
- if (areShortcutsDisabled(editor) || activeElementShouldCaptureKeys(true /* allow buttons */))
261
+ if (
262
+ areShortcutsDisabled(editor) ||
263
+ activeElementShouldCaptureKeys(true /* allow buttons */)
264
+ ) {
138
265
  return
266
+ }
267
+
139
268
  // no accelerator keys
140
269
  if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return
141
270
  const index = NUMBERED_SHORTCUT_KEYS[event.key]
@@ -152,23 +281,23 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
152
281
  }, [editor])
153
282
 
154
283
  const popoverId = 'toolbar overflow'
284
+
285
+ const Layout = orientation === 'horizontal' ? TldrawUiRow : TldrawUiColumn
155
286
  return (
156
287
  <>
157
- <style nonce={editor.options.nonce}>{css}</style>
158
288
  <TldrawUiToolbar
159
- orientation="horizontal"
289
+ orientation={orientation}
160
290
  className={classNames('tlui-main-toolbar__tools', {
161
291
  'tlui-main-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
162
292
  })}
163
293
  label={msg('tool-panel.title')}
164
294
  >
165
- <TldrawUiRow id={`${id}_main`} ref={mainToolsRef}>
295
+ <Layout id={`${id}_main`} ref={mainToolsRef}>
166
296
  <TldrawUiMenuContextProvider type="toolbar" sourceId="toolbar">
167
297
  {children}
168
298
  </TldrawUiMenuContextProvider>
169
- </TldrawUiRow>
170
- {/* There is a +1 because if the menu is just one item, it's not necessary. */}
171
- {totalItems > overflowIndex + 1 && (
299
+ </Layout>
300
+ {shouldShowOverflow && (
172
301
  <IsInOverflowContext.Provider value={true}>
173
302
  <TldrawUiPopover id={popoverId} open={isOpen} onOpenChange={setIsOpen}>
174
303
  <TldrawUiPopoverTrigger>
@@ -178,12 +307,19 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
178
307
  className="tlui-main-toolbar__overflow"
179
308
  data-testid="tools.more-button"
180
309
  >
181
- <TldrawUiButtonIcon icon="chevron-up" />
310
+ <TldrawUiButtonIcon
311
+ icon={orientation === 'horizontal' ? 'chevron-up' : 'chevron-right'}
312
+ />
182
313
  </TldrawUiToolbarButton>
183
314
  </TldrawUiPopoverTrigger>
184
- <TldrawUiPopoverContent side="top" align="center">
315
+ <TldrawUiPopoverContent
316
+ side={orientation === 'horizontal' ? 'top' : 'right'}
317
+ align={orientation === 'horizontal' ? 'center' : 'end'}
318
+ >
185
319
  <TldrawUiToolbar
186
320
  orientation="grid"
321
+ className="tlui-main-toolbar__overflow-content"
322
+ ref={setOverflowTools}
187
323
  data-testid="tools.more-content"
188
324
  label={msg('tool-panel.more')}
189
325
  id={`${id}_more`}
@@ -214,3 +350,19 @@ export const isActiveTLUiToolItem = (
214
350
  ? activeToolId === 'geo' && geoState === item.meta?.geo
215
351
  : activeToolId === item.id
216
352
  }
353
+
354
+ function findParentWithClassName(startingElement: HTMLElement, className: string): HTMLElement {
355
+ let element: HTMLElement | null = startingElement
356
+ while (element) {
357
+ if (element.classList.contains(className)) {
358
+ return element
359
+ }
360
+ element = element.parentElement
361
+ }
362
+ throw new Error('Could not find parent with class name ' + className)
363
+ }
364
+
365
+ function setAttribute(element: HTMLElement, name: string, value: string) {
366
+ if (element.getAttribute(name) === value) return
367
+ element.setAttribute(name, value)
368
+ }
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  DefaultColorStyle,
3
+ getColorValue,
3
4
  SharedStyle,
4
5
  StyleProp,
5
6
  TLDefaultColorStyle,
6
7
  TLDefaultColorTheme,
7
8
  useEditor,
8
9
  } from '@tldraw/editor'
9
- import { ReactElement, memo, useMemo, useRef } from 'react'
10
+ import { memo, ReactElement, useMemo, useRef } from 'react'
10
11
  import { StyleValuesForUi } from '../../../styles'
11
12
  import { PORTRAIT_BREAKPOINT } from '../../constants'
12
13
  import { useBreakpoint } from '../../context/breakpoints'
@@ -140,7 +141,7 @@ export const TldrawUiButtonPicker = memo(function TldrawUiButtonPicker<T extends
140
141
  title={label}
141
142
  style={
142
143
  style === (DefaultColorStyle as StyleProp<unknown>)
143
- ? { color: theme[item.value as TLDefaultColorStyle].solid }
144
+ ? { color: getColorValue(theme, item.value as TLDefaultColorStyle, 'solid') }
144
145
  : undefined
145
146
  }
146
147
  onPointerEnter={handleButtonPointerEnter}
@@ -1,7 +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 { TldrawUiGrid, TldrawUiRow } from './layout'
4
+ import { TldrawUiColumn, TldrawUiGrid, TldrawUiRow } from './layout'
5
5
  import { TldrawUiTooltip } from './TldrawUiTooltip'
6
6
 
7
7
  /** @public */
@@ -10,15 +10,32 @@ export interface TLUiToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
10
10
  className?: string
11
11
  dir?: 'ltr' | 'rtl'
12
12
  label: string
13
- orientation?: 'horizontal' | 'grid'
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,
14
21
  }
15
22
 
16
23
  /** @public @react */
17
24
  export const TldrawUiToolbar = React.forwardRef<HTMLDivElement, TLUiToolbarProps>(
18
- ({ children, className, label, orientation = 'horizontal', ...props }: TLUiToolbarProps, ref) => {
19
- const Layout = orientation === 'grid' ? TldrawUiGrid : TldrawUiRow
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]
20
37
  return (
21
- <Layout asChild>
38
+ <Layout asChild tooltipSide={tooltipSide}>
22
39
  <_Toolbar.Root
23
40
  ref={ref}
24
41
  {...props}