tldraw 3.16.0-canary.a962044c3d3b → 3.16.0-canary.aaf20c977c01

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 (398) hide show
  1. package/dist-cjs/index.d.ts +299 -105
  2. package/dist-cjs/index.js +37 -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/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 +18 -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 +6 -3
  29. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  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/PlainTextLabel.js +1 -3
  35. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  36. package/dist-cjs/lib/shapes/shared/ShapeFill.js +4 -4
  37. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  38. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  39. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +0 -2
  40. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  41. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
  42. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  43. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +2 -2
  44. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  45. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  46. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  47. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  48. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  49. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  50. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  51. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  52. package/dist-cjs/lib/ui/TldrawUi.js +27 -12
  53. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  54. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  55. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  56. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +10 -2
  57. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  58. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  59. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  60. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +6 -6
  61. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  62. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  63. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  64. package/dist-cjs/lib/ui/components/MobileStylePanel.js +5 -3
  65. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  66. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
  67. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  68. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
  69. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  70. package/dist-cjs/lib/ui/components/{primitives/TldrawUiButtonPicker.js → StylePanel/StylePanelButtonPicker.js} +52 -45
  71. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
  72. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
  73. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
  74. package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +23 -22
  75. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
  76. package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +24 -21
  77. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
  78. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
  79. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
  80. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  81. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  82. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +39 -10
  83. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  84. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -22
  85. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  86. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
  87. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
  88. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
  89. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  90. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +188 -78
  91. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  92. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +10 -1
  93. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +17 -4
  95. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  96. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +17 -3
  97. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  98. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +161 -159
  99. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  100. package/dist-cjs/lib/ui/components/primitives/layout.js +30 -5
  101. package/dist-cjs/lib/ui/components/primitives/layout.js.map +2 -2
  102. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  103. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  104. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  105. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +154 -19
  106. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  107. package/dist-cjs/lib/ui/context/actions.js +23 -10
  108. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  109. package/dist-cjs/lib/ui/context/components.js +2 -0
  110. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  111. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  112. package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
  113. package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
  114. package/dist-cjs/lib/ui/hooks/useTools.js +94 -9
  115. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  116. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  117. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  118. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  119. package/dist-cjs/lib/ui/kbd-utils.js +9 -3
  120. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  121. package/dist-cjs/lib/ui/version.js +3 -3
  122. package/dist-cjs/lib/ui/version.js.map +1 -1
  123. package/dist-cjs/lib/utils/export/copyAs.js +1 -2
  124. package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
  125. package/dist-cjs/lib/utils/export/export.js +0 -20
  126. package/dist-cjs/lib/utils/export/export.js.map +2 -2
  127. package/dist-cjs/lib/utils/export/exportAs.js +1 -2
  128. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  129. package/dist-esm/index.d.mts +299 -105
  130. package/dist-esm/index.mjs +70 -30
  131. package/dist-esm/index.mjs.map +2 -2
  132. package/dist-esm/lib/Tldraw.mjs +14 -4
  133. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  134. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  135. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  136. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  137. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  138. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -3
  139. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  140. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  141. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  142. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +4 -3
  143. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  144. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  145. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  146. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +19 -12
  147. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  148. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  149. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  150. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +3 -2
  151. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  152. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs +2 -1
  153. package/dist-esm/lib/shapes/geo/components/GeoShapeBody.mjs.map +2 -2
  154. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +6 -1
  155. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  156. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +6 -3
  157. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  158. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +6 -1
  159. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  160. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -4
  161. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  162. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
  163. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  164. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +5 -4
  165. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  166. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  167. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +0 -2
  168. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  169. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
  170. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  171. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +3 -2
  172. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  173. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  174. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  175. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  176. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  177. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  178. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  179. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  180. package/dist-esm/lib/ui/TldrawUi.mjs +29 -14
  181. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  182. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  183. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  184. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +10 -2
  185. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  186. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  187. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  188. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +6 -6
  189. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  190. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  191. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  192. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +6 -3
  193. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  194. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
  195. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  196. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
  197. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  198. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +126 -0
  199. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
  200. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
  201. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
  202. package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +20 -19
  203. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
  204. package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +21 -18
  205. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
  206. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
  207. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
  208. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  209. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  210. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +39 -10
  211. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  212. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -22
  213. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  214. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
  215. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
  216. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  217. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  218. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -80
  219. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  220. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +10 -1
  221. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  222. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +17 -4
  223. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  224. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +18 -4
  225. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  226. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +170 -161
  227. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  228. package/dist-esm/lib/ui/components/primitives/layout.mjs +31 -6
  229. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +2 -2
  230. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  231. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  232. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  233. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +162 -21
  234. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  235. package/dist-esm/lib/ui/context/actions.mjs +23 -10
  236. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  237. package/dist-esm/lib/ui/context/components.mjs +2 -0
  238. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  239. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  240. package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
  241. package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
  242. package/dist-esm/lib/ui/hooks/useTools.mjs +102 -10
  243. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  244. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  245. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  246. package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
  247. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  248. package/dist-esm/lib/ui/version.mjs +3 -3
  249. package/dist-esm/lib/ui/version.mjs.map +1 -1
  250. package/dist-esm/lib/utils/export/copyAs.mjs +1 -2
  251. package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
  252. package/dist-esm/lib/utils/export/export.mjs +0 -20
  253. package/dist-esm/lib/utils/export/export.mjs.map +2 -2
  254. package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
  255. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  256. package/package.json +11 -34
  257. package/src/index.ts +51 -22
  258. package/src/lib/Tldraw.tsx +15 -2
  259. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  260. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  261. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  262. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  263. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
  264. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -3
  265. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  266. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  267. package/src/lib/shapes/draw/DrawShapeUtil.tsx +4 -3
  268. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  269. package/src/lib/shapes/frame/FrameShapeUtil.tsx +29 -14
  270. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  271. package/src/lib/shapes/geo/GeoShapeUtil.tsx +3 -2
  272. package/src/lib/shapes/geo/components/GeoShapeBody.tsx +2 -2
  273. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +7 -1
  274. package/src/lib/shapes/image/ImageShapeUtil.tsx +6 -3
  275. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  276. package/src/lib/shapes/line/LineShapeUtil.tsx +6 -1
  277. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  278. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -4
  279. package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
  280. package/src/lib/shapes/shared/ShapeFill.tsx +5 -4
  281. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  282. package/src/lib/shapes/shared/useEditablePlainText.ts +0 -6
  283. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
  284. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  285. package/src/lib/shapes/text/TextShapeUtil.tsx +3 -2
  286. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  287. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  288. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  289. package/src/lib/tools/SelectTool/childStates/Translating.ts +0 -1
  290. package/src/lib/ui/TldrawUi.tsx +33 -12
  291. package/src/lib/ui/assetUrls.ts +13 -10
  292. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +13 -2
  293. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  294. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +6 -6
  295. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  296. package/src/lib/ui/components/MobileStylePanel.tsx +9 -6
  297. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
  298. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -381
  299. package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +65 -51
  300. package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
  301. package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +28 -19
  302. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
  303. package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
  304. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  305. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +33 -16
  306. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -24
  307. package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
  308. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
  309. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +208 -56
  310. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +6 -1
  311. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +50 -30
  312. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +25 -5
  313. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +203 -181
  314. package/src/lib/ui/components/primitives/layout.tsx +79 -5
  315. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  316. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  317. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +221 -18
  318. package/src/lib/ui/context/actions.tsx +23 -10
  319. package/src/lib/ui/context/components.tsx +3 -0
  320. package/src/lib/ui/context/events.tsx +2 -1
  321. package/src/lib/ui/hooks/useExportAs.ts +3 -2
  322. package/src/lib/ui/hooks/useTools.tsx +140 -10
  323. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  324. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  325. package/src/lib/ui/kbd-utils.ts +10 -3
  326. package/src/lib/ui/version.ts +3 -3
  327. package/src/lib/ui.css +365 -245
  328. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  329. package/src/lib/utils/export/copyAs.ts +1 -24
  330. package/src/lib/utils/export/export.ts +0 -36
  331. package/src/lib/utils/export/exportAs.ts +1 -32
  332. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  333. package/src/test/A11y.test.tsx +3 -2
  334. package/src/test/ClickManager.test.ts +7 -6
  335. package/src/test/Editor.test.tsx +20 -19
  336. package/src/test/EraserTool.test.ts +184 -13
  337. package/src/test/HandTool.test.ts +10 -9
  338. package/src/test/HighlightShape.test.ts +2 -1
  339. package/src/test/SelectTool.test.ts +3 -2
  340. package/src/test/TLUserPreferences.test.ts +4 -3
  341. package/src/test/TestEditor.ts +13 -15
  342. package/src/test/TldrawEditor.test.tsx +11 -10
  343. package/src/test/ZoomTool.test.ts +7 -6
  344. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  345. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  346. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  347. package/src/test/arrows-megabus.test.tsx +5 -4
  348. package/src/test/bindings.test.tsx +24 -37
  349. package/src/test/bookmark-shapes.test.ts +1 -8
  350. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  351. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  352. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  353. package/src/test/commands/alignShapes.test.tsx +25 -24
  354. package/src/test/commands/animationSpeed.test.ts +2 -1
  355. package/src/test/commands/centerOnPoint.test.ts +3 -2
  356. package/src/test/commands/clipboard.test.ts +3 -2
  357. package/src/test/commands/createShapes.test.ts +2 -1
  358. package/src/test/commands/deleteShapes.test.ts +2 -1
  359. package/src/test/commands/distributeShapes.test.tsx +11 -10
  360. package/src/test/commands/getSvgString.test.ts +2 -1
  361. package/src/test/commands/packShapes.test.ts +5 -4
  362. package/src/test/commands/resizeShape.test.ts +2 -1
  363. package/src/test/commands/rotateShapes.test.ts +7 -6
  364. package/src/test/commands/setCamera.test.ts +4 -3
  365. package/src/test/commands/setCurrentPage.test.ts +3 -2
  366. package/src/test/commands/stackShapes.test.ts +11 -10
  367. package/src/test/commands/stretch.test.tsx +13 -12
  368. package/src/test/createDeepLink.test.tsx +2 -1
  369. package/src/test/cropping.test.ts +3 -2
  370. package/src/test/custom-clipping.test.ts +436 -0
  371. package/src/test/drawing.test.ts +2 -1
  372. package/src/test/flipShapes.test.ts +4 -3
  373. package/src/test/frames.test.ts +25 -24
  374. package/src/test/getCulledShapes.test.tsx +3 -2
  375. package/src/test/groups.test.tsx +1 -1
  376. package/src/test/handleDeepLink.test.tsx +2 -1
  377. package/src/test/maxShapes.test.ts +3 -2
  378. package/src/test/modifiers.test.ts +5 -4
  379. package/src/test/navigation.test.ts +12 -11
  380. package/src/test/panning.test.ts +2 -1
  381. package/src/test/perf/perf.test.ts +2 -1
  382. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  383. package/src/test/resizing.test.ts +39 -38
  384. package/src/test/select.test.tsx +4 -3
  385. package/src/test/selection-omnibus.test.ts +11 -10
  386. package/src/test/shapeutils.test.ts +4 -3
  387. package/src/test/translating.test.ts +9 -8
  388. package/tldraw.css +666 -535
  389. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  390. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
  391. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
  392. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
  393. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
  394. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
  395. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
  396. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs +0 -114
  397. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
  398. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -110
@@ -5,6 +5,7 @@ import { useUiEvents } from '../../context/events'
5
5
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
6
6
  import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
7
7
  import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
8
+ import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
8
9
 
9
10
  /** @public */
10
11
  export interface DefaultVideoToolbarContentProps {
@@ -44,7 +45,12 @@ export const DefaultVideoToolbarContent = track(function DefaultVideoToolbarCont
44
45
  return (
45
46
  <>
46
47
  {!isReadonly && (
47
- <TldrawUiButton type="icon" title={msg('tool.replace-media')} onClick={handleVideoReplace}>
48
+ <TldrawUiButton
49
+ type="icon"
50
+ title={msg('tool.replace-media')}
51
+ onClick={handleVideoReplace}
52
+ data-testid="tool.video-replace"
53
+ >
48
54
  <TldrawUiButtonIcon small icon="tool-media" />
49
55
  </TldrawUiButton>
50
56
  )}
@@ -52,21 +58,23 @@ export const DefaultVideoToolbarContent = track(function DefaultVideoToolbarCont
52
58
  type="icon"
53
59
  title={msg('action.download-original')}
54
60
  onClick={handleVideoDownload}
61
+ data-testid="tool.video-download"
55
62
  >
56
63
  <TldrawUiButtonIcon small icon="download" />
57
64
  </TldrawUiButton>
58
65
  {(altText || !isReadonly) && (
59
- <TldrawUiButton
60
- type="normal"
66
+ <TldrawUiToolbarButton
67
+ type="icon"
61
68
  isActive={!!altText}
62
69
  title={msg('tool.media-alt-text')}
70
+ data-testid="tool.video-alt-text"
63
71
  onClick={() => {
64
72
  trackEvent('alt-text-start', { source })
65
73
  onEditAltTextStart()
66
74
  }}
67
75
  >
68
76
  <TldrawUiButtonIcon small icon="alt" />
69
- </TldrawUiButton>
77
+ </TldrawUiToolbarButton>
70
78
  )}
71
79
  </>
72
80
  )
@@ -2,9 +2,9 @@ import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'
2
2
  import { useEffect, useRef, useState } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
5
- import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
6
5
  import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
7
6
  import { TldrawUiInput } from '../primitives/TldrawUiInput'
7
+ import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
8
8
 
9
9
  /** @public */
10
10
  export interface LinkEditorProps {
@@ -76,7 +76,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
76
76
  onCancel={handleLinkCancel}
77
77
  placeholder="example.com"
78
78
  />
79
- <TldrawUiButton
79
+ <TldrawUiToolbarButton
80
80
  className="tlui-rich-text__toolbar-link-visit"
81
81
  title={msg('tool.rich-text-link-visit')}
82
82
  type="icon"
@@ -85,8 +85,8 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
85
85
  disabled={!value}
86
86
  >
87
87
  <TldrawUiButtonIcon small icon="external-link" />
88
- </TldrawUiButton>
89
- <TldrawUiButton
88
+ </TldrawUiToolbarButton>
89
+ <TldrawUiToolbarButton
90
90
  className="tlui-rich-text__toolbar-link-remove"
91
91
  title={msg('tool.rich-text-link-remove')}
92
92
  data-testid="rich-text.link-remove"
@@ -95,7 +95,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
95
95
  onClick={handleRemoveLink}
96
96
  >
97
97
  <TldrawUiButtonIcon small icon="trash" />
98
- </TldrawUiButton>
98
+ </TldrawUiToolbarButton>
99
99
  </>
100
100
  )
101
101
  }
@@ -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
+ }
@@ -172,7 +172,12 @@ export const TldrawUiContextualToolbar = ({
172
172
  className={classNames('tlui-contextual-toolbar', className)}
173
173
  onPointerDown={stopEventPropagation}
174
174
  >
175
- <TldrawUiToolbar orientation="horizontal" className="tlui-menu" label={label}>
175
+ <TldrawUiToolbar
176
+ orientation="horizontal"
177
+ className="tlui-menu"
178
+ label={label}
179
+ tooltipSide="top"
180
+ >
176
181
  {children}
177
182
  </TldrawUiToolbar>
178
183
  </div>
@@ -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,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}
@@ -77,6 +94,7 @@ export interface TLUiToolbarToggleGroupProps extends React.HTMLAttributes<HTMLDi
77
94
  // TODO: fix up this type later
78
95
  defaultValue?: any
79
96
  type: 'single' | 'multiple'
97
+ asChild?: boolean
80
98
  }
81
99
 
82
100
  /** @public @react */
@@ -84,10 +102,12 @@ export const TldrawUiToolbarToggleGroup = ({
84
102
  children,
85
103
  className,
86
104
  type,
105
+ asChild,
87
106
  ...props
88
107
  }: TLUiToolbarToggleGroupProps) => {
89
108
  return (
90
109
  <_Toolbar.ToggleGroup
110
+ asChild={asChild}
91
111
  type={type}
92
112
  {...props}
93
113
  // TODO: this fixes a bug in Radix until they fix it.