tldraw 3.16.0-next.34fddf633325 → 3.16.0-next.6611943ca24a

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 (320) hide show
  1. package/dist-cjs/index.d.ts +173 -5
  2. package/dist-cjs/index.js +12 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
  5. package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
  6. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +3 -3
  7. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
  9. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  10. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
  11. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  13. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  14. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +12 -12
  15. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  17. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  18. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +2 -2
  19. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  20. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js +2 -1
  21. package/dist-cjs/lib/shapes/geo/components/GeoShapeBody.js.map +2 -2
  22. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +5 -1
  23. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
  25. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
  26. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +5 -1
  27. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  28. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +4 -4
  29. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  30. package/dist-cjs/lib/shapes/shared/ShapeFill.js +4 -4
  31. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  32. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  33. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
  34. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  36. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  37. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  38. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  39. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  40. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  41. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  42. package/dist-cjs/lib/ui/TldrawUi.js +14 -0
  43. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  44. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  45. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  46. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +12 -3
  47. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  48. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js +3 -2
  49. package/dist-cjs/lib/ui/components/DefaultMenuPanel.js.map +2 -2
  50. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +1 -1
  51. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  52. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  53. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  54. package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
  55. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  56. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +1 -1
  57. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
  58. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +2 -1
  59. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
  60. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js +3 -2
  61. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenuItem.js.map +2 -2
  62. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js +2 -2
  63. package/dist-cjs/lib/ui/components/SharePanel/UserPresenceColorPicker.js.map +2 -2
  64. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +14 -14
  65. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  66. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js +3 -3
  67. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +2 -2
  68. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js +26 -25
  69. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +3 -3
  70. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
  71. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  72. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -21
  73. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  74. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +189 -80
  75. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  76. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +2 -2
  77. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
  78. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js +5 -16
  79. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +3 -3
  80. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +1 -1
  81. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  82. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +3 -2
  83. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +3 -3
  84. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +19 -4
  85. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  86. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +153 -152
  87. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  88. package/dist-cjs/lib/ui/components/primitives/layout.js +76 -0
  89. package/dist-cjs/lib/ui/components/primitives/layout.js.map +7 -0
  90. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  91. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  92. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  93. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +154 -20
  94. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  95. package/dist-cjs/lib/ui/context/actions.js +16 -2
  96. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  97. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  98. package/dist-cjs/lib/ui/hooks/useTools.js +94 -9
  99. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  100. package/dist-cjs/lib/ui/version.js +3 -3
  101. package/dist-cjs/lib/ui/version.js.map +1 -1
  102. package/dist-esm/index.d.mts +173 -5
  103. package/dist-esm/index.mjs +19 -1
  104. package/dist-esm/index.mjs.map +2 -2
  105. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  106. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  107. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -3
  108. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  109. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  110. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  111. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
  112. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  113. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  114. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  115. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +13 -12
  116. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  117. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  118. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  119. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
  120. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  121. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
  122. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
  123. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
  124. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  125. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
  126. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
  127. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +6 -1
  128. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  129. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
  130. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  131. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +5 -4
  132. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  133. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  134. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
  135. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  136. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  137. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  138. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  139. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  140. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  141. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  142. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  143. package/dist-esm/lib/ui/TldrawUi.mjs +16 -2
  144. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  145. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  146. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  147. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +12 -3
  148. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  149. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs +3 -2
  150. package/dist-esm/lib/ui/components/DefaultMenuPanel.mjs.map +2 -2
  151. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +1 -1
  152. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  153. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  154. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  155. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
  156. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  157. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +1 -1
  158. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
  159. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +2 -1
  160. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
  161. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs +3 -2
  162. package/dist-esm/lib/ui/components/SharePanel/PeopleMenuItem.mjs.map +2 -2
  163. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs +2 -2
  164. package/dist-esm/lib/ui/components/SharePanel/UserPresenceColorPicker.mjs.map +2 -2
  165. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +14 -14
  166. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  167. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs +3 -3
  168. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +2 -2
  169. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs +26 -25
  170. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +2 -2
  171. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
  172. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  173. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -21
  174. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  175. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -81
  176. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  177. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +2 -2
  178. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
  179. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +6 -6
  180. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +2 -2
  181. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +1 -1
  182. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  183. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +3 -2
  184. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
  185. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +19 -4
  186. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  187. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +162 -154
  188. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  189. package/dist-esm/lib/ui/components/primitives/layout.mjs +46 -0
  190. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +7 -0
  191. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  192. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  193. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  194. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +162 -22
  195. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  196. package/dist-esm/lib/ui/context/actions.mjs +16 -2
  197. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  198. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  199. package/dist-esm/lib/ui/hooks/useTools.mjs +102 -10
  200. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  201. package/dist-esm/lib/ui/version.mjs +3 -3
  202. package/dist-esm/lib/ui/version.mjs.map +1 -1
  203. package/package.json +11 -34
  204. package/src/index.ts +15 -0
  205. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  206. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  207. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  208. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
  209. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -3
  210. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  211. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  212. package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
  213. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  214. package/src/lib/shapes/frame/FrameShapeUtil.tsx +21 -14
  215. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  216. package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
  217. package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
  218. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
  219. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
  220. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  221. package/src/lib/shapes/line/LineShapeUtil.tsx +6 -1
  222. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  223. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
  224. package/src/lib/shapes/shared/ShapeFill.tsx +5 -4
  225. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  226. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  227. package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
  228. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  229. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  230. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  231. package/src/lib/tools/SelectTool/childStates/Translating.ts +0 -1
  232. package/src/lib/ui/TldrawUi.tsx +17 -2
  233. package/src/lib/ui/assetUrls.ts +13 -10
  234. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +15 -3
  235. package/src/lib/ui/components/DefaultMenuPanel.tsx +4 -3
  236. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +1 -1
  237. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  238. package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
  239. package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +1 -1
  240. package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +3 -2
  241. package/src/lib/ui/components/SharePanel/PeopleMenuItem.tsx +4 -3
  242. package/src/lib/ui/components/SharePanel/UserPresenceColorPicker.tsx +3 -3
  243. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +39 -43
  244. package/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx +3 -3
  245. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +7 -6
  246. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
  247. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -23
  248. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +212 -61
  249. package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +2 -2
  250. package/src/lib/ui/components/primitives/TldrawUiButtonPicker.tsx +40 -37
  251. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +1 -1
  252. package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +4 -2
  253. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +32 -9
  254. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +186 -172
  255. package/src/lib/ui/components/primitives/layout.tsx +107 -0
  256. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  257. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  258. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +221 -19
  259. package/src/lib/ui/context/actions.tsx +16 -2
  260. package/src/lib/ui/context/events.tsx +1 -0
  261. package/src/lib/ui/hooks/useTools.tsx +140 -10
  262. package/src/lib/ui/version.ts +3 -3
  263. package/src/lib/ui.css +366 -305
  264. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  265. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  266. package/src/test/A11y.test.tsx +3 -2
  267. package/src/test/ClickManager.test.ts +7 -6
  268. package/src/test/Editor.test.tsx +20 -19
  269. package/src/test/EraserTool.test.ts +184 -13
  270. package/src/test/HandTool.test.ts +10 -9
  271. package/src/test/HighlightShape.test.ts +2 -1
  272. package/src/test/SelectTool.test.ts +3 -2
  273. package/src/test/TLUserPreferences.test.ts +4 -3
  274. package/src/test/TestEditor.ts +13 -15
  275. package/src/test/TldrawEditor.test.tsx +11 -10
  276. package/src/test/ZoomTool.test.ts +7 -6
  277. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  278. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  279. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  280. package/src/test/arrows-megabus.test.tsx +5 -4
  281. package/src/test/bindings.test.tsx +24 -37
  282. package/src/test/bookmark-shapes.test.ts +1 -8
  283. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  284. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  285. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  286. package/src/test/commands/alignShapes.test.tsx +25 -24
  287. package/src/test/commands/animationSpeed.test.ts +2 -1
  288. package/src/test/commands/centerOnPoint.test.ts +3 -2
  289. package/src/test/commands/clipboard.test.ts +3 -2
  290. package/src/test/commands/createShapes.test.ts +2 -1
  291. package/src/test/commands/deleteShapes.test.ts +2 -1
  292. package/src/test/commands/distributeShapes.test.tsx +11 -10
  293. package/src/test/commands/getSvgString.test.ts +2 -1
  294. package/src/test/commands/packShapes.test.ts +5 -4
  295. package/src/test/commands/resizeShape.test.ts +2 -1
  296. package/src/test/commands/rotateShapes.test.ts +7 -6
  297. package/src/test/commands/setCamera.test.ts +4 -3
  298. package/src/test/commands/setCurrentPage.test.ts +3 -2
  299. package/src/test/commands/stackShapes.test.ts +11 -10
  300. package/src/test/commands/stretch.test.tsx +13 -12
  301. package/src/test/createDeepLink.test.tsx +2 -1
  302. package/src/test/cropping.test.ts +3 -2
  303. package/src/test/drawing.test.ts +2 -1
  304. package/src/test/flipShapes.test.ts +4 -3
  305. package/src/test/frames.test.ts +25 -24
  306. package/src/test/getCulledShapes.test.tsx +3 -2
  307. package/src/test/groups.test.tsx +1 -1
  308. package/src/test/handleDeepLink.test.tsx +2 -1
  309. package/src/test/maxShapes.test.ts +3 -2
  310. package/src/test/modifiers.test.ts +5 -4
  311. package/src/test/navigation.test.ts +12 -11
  312. package/src/test/panning.test.ts +2 -1
  313. package/src/test/perf/perf.test.ts +2 -1
  314. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  315. package/src/test/resizing.test.ts +39 -38
  316. package/src/test/select.test.tsx +4 -3
  317. package/src/test/selection-omnibus.test.ts +11 -10
  318. package/src/test/shapeutils.test.ts +4 -3
  319. package/src/test/translating.test.ts +9 -8
  320. package/tldraw.css +659 -595
@@ -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,6 +22,7 @@ import {
20
22
  TldrawUiPopoverTrigger,
21
23
  } from '../primitives/TldrawUiPopover'
22
24
  import { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
25
+ import { TldrawUiColumn, TldrawUiRow } from '../primitives/layout'
23
26
  import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
24
27
 
25
28
  export const IsInOverflowContext = createContext(false)
@@ -40,10 +43,24 @@ const NUMBERED_SHORTCUT_KEYS: Record<string, number> = {
40
43
  /** @public */
41
44
  export interface OverflowingToolbarProps {
42
45
  children: React.ReactNode
46
+ orientation: 'horizontal' | 'vertical'
47
+ sizingParentClassName: string
48
+ minItems: number
49
+ minSizePx: number
50
+ maxItems: number
51
+ maxSizePx: number
43
52
  }
44
53
 
45
54
  /** @public @react */
46
- 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) {
47
64
  const editor = useEditor()
48
65
  const id = useUniqueSafeId()
49
66
  const breakpoint = useBreakpoint()
@@ -51,66 +68,165 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
51
68
  const rButtons = useRef<HTMLElement[]>([])
52
69
  const [isOpen, setIsOpen] = useState(false)
53
70
 
54
- const overflowIndex = Math.min(8, 5 + breakpoint)
55
-
56
- const [totalItems, setTotalItems] = useState(0)
57
71
  const mainToolsRef = useRef<HTMLDivElement>(null)
58
- const [lastActiveOverflowItem, setLastActiveOverflowItem] = useState<string | null>(null)
59
-
60
- const css = useMemo(() => {
61
- const activeCss = lastActiveOverflowItem ? `:not([data-value="${lastActiveOverflowItem}"])` : ''
62
72
 
63
- return `
64
- #${id}_main > *:nth-of-type(n + ${overflowIndex + (lastActiveOverflowItem ? 1 : 2)}):not([data-radix-popper-content-wrapper])${activeCss} {
65
- display: none;
66
- }
67
- #${id}_more > *:nth-of-type(-n + ${overflowIndex}):not([data-radix-popper-content-wrapper]) {
68
- display: none;
69
- }
70
- #${id}_more > *:nth-of-type(-n + ${overflowIndex + 4}):not([data-radix-popper-content-wrapper]) {
71
- margin-top: 0;
72
- }
73
- `
74
- }, [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)
75
78
 
76
79
  const onDomUpdate = useEvent(() => {
77
80
  if (!mainToolsRef.current) return
78
81
 
79
- const children = Array.from(mainToolsRef.current.children)
80
- 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'
81
85
 
82
- // If the last active overflow item is no longer in the overflow, clear it
83
- const lastActiveElementIdx = children.findIndex(
84
- (el) => el.getAttribute('data-value') === lastActiveOverflowItem
85
- )
86
- if (lastActiveElementIdx <= overflowIndex) {
87
- 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
88
110
  }
89
111
 
90
- // But if there's a new active item...
91
- const activeElementIdx = Array.from(mainToolsRef.current.children).findIndex(
92
- (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)
93
117
  )
94
- if (activeElementIdx === -1) return
95
118
 
96
- // ...and it's in the overflow, set it as the last active overflow item
97
- if (activeElementIdx >= overflowIndex) {
98
- setLastActiveOverflowItem(children[activeElementIdx].getAttribute('data-value'))
99
- }
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
100
162
 
101
- // Save the buttons that are actually visible
102
- rButtons.current = Array.from(mainToolsRef.current?.children ?? []).filter(
103
- (el): el is HTMLElement => {
104
- // only count html elements...
105
- if (!(el instanceof HTMLElement)) return false
163
+ didShowAnyInMain ||= shouldShowInMain
164
+ didShowAnyInOverflow ||= shouldShowInOverflow
106
165
 
107
- // ...that are buttons...
108
- 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
+ }
109
200
 
110
- // ...that are actually visible
111
- 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
+ }
112
217
  }
113
- )
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
114
230
  })
115
231
 
116
232
  useLayoutEffect(() => {
@@ -124,20 +240,31 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
124
240
  mutationObserver.observe(mainToolsRef.current, {
125
241
  childList: true,
126
242
  subtree: true,
127
- attributeFilter: ['data-value', 'aria-pressed'],
243
+ attributes: true,
244
+ characterData: true,
128
245
  })
129
246
 
247
+ const sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)
248
+ const resizeObserver = new ResizeObserver(onDomUpdate)
249
+ resizeObserver.observe(sizingParent)
250
+
130
251
  return () => {
131
252
  mutationObserver.disconnect()
253
+ resizeObserver.disconnect()
132
254
  }
133
- }, [onDomUpdate])
255
+ }, [onDomUpdate, sizingParentClassName])
134
256
 
135
257
  useEffect(() => {
136
258
  if (!editor.options.enableToolbarKeyboardShortcuts) return
137
259
 
138
260
  function handleKeyDown(event: KeyboardEvent) {
139
- if (areShortcutsDisabled(editor) || activeElementShouldCaptureKeys(true /* allow buttons */))
261
+ if (
262
+ areShortcutsDisabled(editor) ||
263
+ activeElementShouldCaptureKeys(true /* allow buttons */)
264
+ ) {
140
265
  return
266
+ }
267
+
141
268
  // no accelerator keys
142
269
  if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return
143
270
  const index = NUMBERED_SHORTCUT_KEYS[event.key]
@@ -154,37 +281,45 @@ export function OverflowingToolbar({ children }: OverflowingToolbarProps) {
154
281
  }, [editor])
155
282
 
156
283
  const popoverId = 'toolbar overflow'
284
+
285
+ const Layout = orientation === 'horizontal' ? TldrawUiRow : TldrawUiColumn
157
286
  return (
158
287
  <>
159
- <style nonce={editor.options.nonce}>{css}</style>
160
288
  <TldrawUiToolbar
161
- className={classNames('tlui-toolbar__tools', {
162
- 'tlui-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
289
+ orientation={orientation}
290
+ className={classNames('tlui-main-toolbar__tools', {
291
+ 'tlui-main-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
163
292
  })}
164
293
  label={msg('tool-panel.title')}
165
294
  >
166
- <div id={`${id}_main`} ref={mainToolsRef} className="tlui-toolbar__tools__list">
295
+ <Layout id={`${id}_main`} ref={mainToolsRef}>
167
296
  <TldrawUiMenuContextProvider type="toolbar" sourceId="toolbar">
168
297
  {children}
169
298
  </TldrawUiMenuContextProvider>
170
- </div>
171
- {/* There is a +1 because if the menu is just one item, it's not necessary. */}
172
- {totalItems > overflowIndex + 1 && (
299
+ </Layout>
300
+ {shouldShowOverflow && (
173
301
  <IsInOverflowContext.Provider value={true}>
174
302
  <TldrawUiPopover id={popoverId} open={isOpen} onOpenChange={setIsOpen}>
175
303
  <TldrawUiPopoverTrigger>
176
304
  <TldrawUiToolbarButton
177
305
  title={msg('tool-panel.more')}
178
306
  type="tool"
179
- className="tlui-toolbar__overflow"
307
+ className="tlui-main-toolbar__overflow"
180
308
  data-testid="tools.more-button"
181
309
  >
182
- <TldrawUiButtonIcon icon="chevron-up" />
310
+ <TldrawUiButtonIcon
311
+ icon={orientation === 'horizontal' ? 'chevron-up' : 'chevron-right'}
312
+ />
183
313
  </TldrawUiToolbarButton>
184
314
  </TldrawUiPopoverTrigger>
185
- <TldrawUiPopoverContent side="top" align="center">
315
+ <TldrawUiPopoverContent
316
+ side={orientation === 'horizontal' ? 'top' : 'right'}
317
+ align={orientation === 'horizontal' ? 'center' : 'end'}
318
+ >
186
319
  <TldrawUiToolbar
187
- className="tlui-buttons__grid"
320
+ orientation="grid"
321
+ className="tlui-main-toolbar__overflow-content"
322
+ ref={setOverflowTools}
188
323
  data-testid="tools.more-content"
189
324
  label={msg('tool-panel.more')}
190
325
  id={`${id}_more`}
@@ -215,3 +350,19 @@ export const isActiveTLUiToolItem = (
215
350
  ? activeToolId === 'geo' && geoState === item.meta?.geo
216
351
  : activeToolId === item.id
217
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
+ }
@@ -31,8 +31,8 @@ export function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonP
31
31
  type="normal"
32
32
  title={msg('action.toggle-tool-lock')}
33
33
  data-testid="tool-lock"
34
- className={classNames('tlui-toolbar__lock-button', {
35
- 'tlui-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
34
+ className={classNames('tlui-main-toolbar__lock-button', {
35
+ 'tlui-main-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
36
36
  })}
37
37
  onClick={() => editor.updateInstanceState({ isToolLocked: !isToolLocked })}
38
38
  >
@@ -1,13 +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 classNames from 'classnames'
10
- import { ReactElement, memo, useMemo, useRef } from 'react'
10
+ import { memo, ReactElement, useMemo, useRef } from 'react'
11
11
  import { StyleValuesForUi } from '../../../styles'
12
12
  import { PORTRAIT_BREAKPOINT } from '../../constants'
13
13
  import { useBreakpoint } from '../../context/breakpoints'
@@ -15,6 +15,7 @@ import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKe
15
15
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
16
16
  import { TldrawUiButtonIcon } from './Button/TldrawUiButtonIcon'
17
17
  import { TldrawUiToolbarToggleGroup, TldrawUiToolbarToggleItem } from './TldrawUiToolbar'
18
+ import { TldrawUiGrid, TldrawUiRow } from './layout'
18
19
 
19
20
  /** @public */
20
21
  export interface TLUiButtonPickerProps<T extends string> {
@@ -116,41 +117,43 @@ export const TldrawUiButtonPicker = memo(function TldrawUiButtonPicker<T extends
116
117
  }
117
118
  }, [editor, breakpoint, value, onHistoryMark, onValueChange, style])
118
119
 
120
+ const Wrapper = items.length > 4 ? TldrawUiGrid : TldrawUiRow
121
+
119
122
  return (
120
- <TldrawUiToolbarToggleGroup
121
- data-testid={`style.${uiType}`}
122
- type="single"
123
- className={classNames('tlui-buttons__grid')}
124
- value={value.type === 'shared' ? value.value : undefined}
125
- >
126
- {items.map((item) => {
127
- const label = title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
128
- return (
129
- <TldrawUiToolbarToggleItem
130
- type="icon"
131
- key={item.value}
132
- data-id={item.value}
133
- data-testid={`style.${uiType}.${item.value}`}
134
- aria-label={label}
135
- value={item.value}
136
- data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'}
137
- data-isactive={value.type === 'shared' && value.value === item.value}
138
- title={label}
139
- className={classNames('tlui-button-grid__button')}
140
- style={
141
- style === (DefaultColorStyle as StyleProp<unknown>)
142
- ? { color: theme[item.value as TLDefaultColorStyle].solid }
143
- : undefined
144
- }
145
- onPointerEnter={handleButtonPointerEnter}
146
- onPointerDown={handleButtonPointerDown}
147
- onPointerUp={handleButtonPointerUp}
148
- onClick={handleButtonClick}
149
- >
150
- <TldrawUiButtonIcon icon={item.icon} />
151
- </TldrawUiToolbarToggleItem>
152
- )
153
- })}
154
- </TldrawUiToolbarToggleGroup>
123
+ <Wrapper asChild>
124
+ <TldrawUiToolbarToggleGroup
125
+ data-testid={`style.${uiType}`}
126
+ type="single"
127
+ value={value.type === 'shared' ? value.value : undefined}
128
+ >
129
+ {items.map((item) => {
130
+ const label = title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
131
+ return (
132
+ <TldrawUiToolbarToggleItem
133
+ type="icon"
134
+ key={item.value}
135
+ data-id={item.value}
136
+ data-testid={`style.${uiType}.${item.value}`}
137
+ aria-label={label}
138
+ value={item.value}
139
+ data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'}
140
+ data-isactive={value.type === 'shared' && value.value === item.value}
141
+ title={label}
142
+ style={
143
+ style === (DefaultColorStyle as StyleProp<unknown>)
144
+ ? { color: getColorValue(theme, item.value as TLDefaultColorStyle, 'solid') }
145
+ : undefined
146
+ }
147
+ onPointerEnter={handleButtonPointerEnter}
148
+ onPointerDown={handleButtonPointerDown}
149
+ onPointerUp={handleButtonPointerUp}
150
+ onClick={handleButtonClick}
151
+ >
152
+ <TldrawUiButtonIcon icon={item.icon} />
153
+ </TldrawUiToolbarToggleItem>
154
+ )
155
+ })}
156
+ </TldrawUiToolbarToggleGroup>
157
+ </Wrapper>
155
158
  )
156
159
  }) as <T extends string>(props: TLUiButtonPickerProps<T>) => ReactElement
@@ -172,7 +172,7 @@ export const TldrawUiContextualToolbar = ({
172
172
  className={classNames('tlui-contextual-toolbar', className)}
173
173
  onPointerDown={stopEventPropagation}
174
174
  >
175
- <TldrawUiToolbar className="tlui-menu tlui-buttons__horizontal" label={label}>
175
+ <TldrawUiToolbar orientation="horizontal" className="tlui-menu" label={label}>
176
176
  {children}
177
177
  </TldrawUiToolbar>
178
178
  </div>
@@ -1,4 +1,5 @@
1
1
  import { useContainer } from '@tldraw/editor'
2
+ import classNames from 'classnames'
2
3
  import { Popover as _Popover } from 'radix-ui'
3
4
  import React from 'react'
4
5
  import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
@@ -9,15 +10,16 @@ export interface TLUiPopoverProps {
9
10
  open?: boolean
10
11
  children: React.ReactNode
11
12
  onOpenChange?(isOpen: boolean): void
13
+ className?: string
12
14
  }
13
15
 
14
16
  /** @public @react */
15
- export function TldrawUiPopover({ id, children, onOpenChange, open }: TLUiPopoverProps) {
17
+ export function TldrawUiPopover({ id, children, onOpenChange, open, className }: TLUiPopoverProps) {
16
18
  const [isOpen, handleOpenChange] = useMenuIsOpen(id, onOpenChange)
17
19
 
18
20
  return (
19
21
  <_Popover.Root onOpenChange={handleOpenChange} open={open || isOpen /* allow debugging */}>
20
- <div className="tlui-popover">{children}</div>
22
+ <div className={classNames('tlui-popover', className)}>{children}</div>
21
23
  </_Popover.Root>
22
24
  )
23
25
  }
@@ -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
  )