tldraw 3.16.0-next.282b7be564ae → 3.16.0-next.2f9d39693e4c

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 (291) hide show
  1. package/dist-cjs/index.d.ts +224 -102
  2. package/dist-cjs/index.js +33 -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/defaultExternalContentHandlers.js +5 -4
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +6 -0
  9. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +3 -3
  10. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +6 -0
  11. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -0
  13. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -3
  15. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  16. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  17. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +0 -2
  18. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  19. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +0 -2
  20. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  21. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +25 -1
  22. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  23. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +12 -0
  24. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  25. package/dist-cjs/lib/ui/TldrawUi.js +13 -12
  26. package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
  27. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  28. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  29. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  30. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  31. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +6 -6
  32. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  33. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js +9 -4
  34. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanel.js.map +2 -2
  35. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +255 -316
  36. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  37. package/dist-cjs/lib/ui/components/{primitives/TldrawUiButtonPicker.js → StylePanel/StylePanelButtonPicker.js} +52 -45
  38. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +7 -0
  39. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js +68 -0
  40. package/dist-cjs/lib/ui/components/StylePanel/StylePanelContext.js.map +7 -0
  41. package/dist-cjs/lib/ui/components/StylePanel/{DoubleDropdownPicker.js → StylePanelDoubleDropdownPicker.js} +23 -22
  42. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +7 -0
  43. package/dist-cjs/lib/ui/components/StylePanel/{DropdownPicker.js → StylePanelDropdownPicker.js} +24 -21
  44. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +7 -0
  45. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js +28 -0
  46. package/dist-cjs/lib/ui/components/StylePanel/StylePanelSubheading.js.map +7 -0
  47. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  48. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  49. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +38 -9
  50. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  51. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
  52. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
  53. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
  54. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  55. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +10 -1
  56. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  57. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +17 -4
  58. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  59. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +2 -0
  60. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  61. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +37 -36
  62. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  63. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +2 -1
  64. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  65. package/dist-cjs/lib/ui/context/actions.js +23 -10
  66. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  67. package/dist-cjs/lib/ui/context/components.js +2 -0
  68. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  69. package/dist-cjs/lib/ui/context/events.js.map +1 -1
  70. package/dist-cjs/lib/ui/hooks/useExportAs.js +3 -2
  71. package/dist-cjs/lib/ui/hooks/useExportAs.js.map +2 -2
  72. package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
  73. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  74. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  75. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  76. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  77. package/dist-cjs/lib/ui/kbd-utils.js +9 -3
  78. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  79. package/dist-cjs/lib/ui/version.js +3 -3
  80. package/dist-cjs/lib/ui/version.js.map +1 -1
  81. package/dist-cjs/lib/utils/export/copyAs.js +1 -2
  82. package/dist-cjs/lib/utils/export/copyAs.js.map +2 -2
  83. package/dist-cjs/lib/utils/export/export.js +0 -20
  84. package/dist-cjs/lib/utils/export/export.js.map +2 -2
  85. package/dist-cjs/lib/utils/export/exportAs.js +1 -2
  86. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  87. package/dist-esm/index.d.mts +224 -102
  88. package/dist-esm/index.mjs +61 -29
  89. package/dist-esm/index.mjs.map +2 -2
  90. package/dist-esm/lib/Tldraw.mjs +14 -4
  91. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  92. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  93. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  94. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +6 -0
  95. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +3 -3
  96. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +6 -0
  97. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  98. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -0
  99. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  100. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -3
  101. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  102. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  103. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +0 -2
  104. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  105. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +0 -2
  106. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  107. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +26 -1
  108. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  109. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +13 -0
  110. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  111. package/dist-esm/lib/ui/TldrawUi.mjs +13 -12
  112. package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
  113. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  114. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  115. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  116. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  117. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +6 -6
  118. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  119. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs +14 -5
  120. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanel.mjs.map +2 -2
  121. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +257 -320
  122. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  123. package/dist-esm/lib/ui/components/{primitives/TldrawUiButtonPicker.mjs → StylePanel/StylePanelButtonPicker.mjs} +54 -43
  124. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +7 -0
  125. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs +48 -0
  126. package/dist-esm/lib/ui/components/StylePanel/StylePanelContext.mjs.map +7 -0
  127. package/dist-esm/lib/ui/components/StylePanel/{DoubleDropdownPicker.mjs → StylePanelDoubleDropdownPicker.mjs} +20 -19
  128. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +7 -0
  129. package/dist-esm/lib/ui/components/StylePanel/{DropdownPicker.mjs → StylePanelDropdownPicker.mjs} +21 -18
  130. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +7 -0
  131. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs +8 -0
  132. package/dist-esm/lib/ui/components/StylePanel/StylePanelSubheading.mjs.map +7 -0
  133. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  134. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  135. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +38 -9
  136. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  137. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
  138. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
  139. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  140. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  141. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +10 -1
  142. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  143. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +17 -4
  144. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  145. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +2 -0
  146. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  147. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +37 -36
  148. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  149. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -1
  150. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  151. package/dist-esm/lib/ui/context/actions.mjs +23 -10
  152. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  153. package/dist-esm/lib/ui/context/components.mjs +2 -0
  154. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  155. package/dist-esm/lib/ui/context/events.mjs.map +1 -1
  156. package/dist-esm/lib/ui/hooks/useExportAs.mjs +3 -2
  157. package/dist-esm/lib/ui/hooks/useExportAs.mjs.map +2 -2
  158. package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
  159. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  160. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  161. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  162. package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
  163. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  164. package/dist-esm/lib/ui/version.mjs +3 -3
  165. package/dist-esm/lib/ui/version.mjs.map +1 -1
  166. package/dist-esm/lib/utils/export/copyAs.mjs +1 -2
  167. package/dist-esm/lib/utils/export/copyAs.mjs.map +2 -2
  168. package/dist-esm/lib/utils/export/export.mjs +0 -20
  169. package/dist-esm/lib/utils/export/export.mjs.map +2 -2
  170. package/dist-esm/lib/utils/export/exportAs.mjs +1 -2
  171. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  172. package/package.json +11 -34
  173. package/src/index.ts +44 -22
  174. package/src/lib/Tldraw.tsx +15 -2
  175. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  176. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +2 -1
  177. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +4 -3
  178. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +7 -6
  179. package/src/lib/shapes/arrow/arrowLabel.ts +8 -0
  180. package/src/lib/shapes/draw/DrawShapeTool.test.ts +0 -5
  181. package/src/lib/shapes/frame/FrameShapeUtil.tsx +8 -0
  182. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -0
  183. package/src/lib/shapes/line/LineShapeUtil.test.tsx +4 -3
  184. package/src/lib/shapes/line/__snapshots__/LineShapeUtil.test.tsx.snap +2 -2
  185. package/src/lib/shapes/shared/PlainTextLabel.tsx +0 -6
  186. package/src/lib/shapes/shared/freehand/svg.ts +2 -0
  187. package/src/lib/shapes/shared/useEditablePlainText.ts +0 -6
  188. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +0 -7
  189. package/src/lib/shapes/text/TextShapeTool.test.ts +6 -5
  190. package/src/lib/tools/EraserTool/childStates/Erasing.ts +34 -1
  191. package/src/lib/tools/EraserTool/childStates/Pointing.ts +20 -0
  192. package/src/lib/ui/TldrawUi.tsx +16 -10
  193. package/src/lib/ui/assetUrls.ts +13 -10
  194. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  195. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +6 -6
  196. package/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx +27 -13
  197. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +260 -381
  198. package/src/lib/ui/components/{primitives/TldrawUiButtonPicker.tsx → StylePanel/StylePanelButtonPicker.tsx} +63 -50
  199. package/src/lib/ui/components/StylePanel/StylePanelContext.tsx +63 -0
  200. package/src/lib/ui/components/StylePanel/{DoubleDropdownPicker.tsx → StylePanelDoubleDropdownPicker.tsx} +28 -19
  201. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +119 -0
  202. package/src/lib/ui/components/StylePanel/StylePanelSubheading.tsx +9 -0
  203. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  204. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +32 -15
  205. package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
  206. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
  207. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +6 -1
  208. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +50 -30
  209. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +3 -0
  210. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +29 -21
  211. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +3 -2
  212. package/src/lib/ui/context/actions.tsx +23 -10
  213. package/src/lib/ui/context/components.tsx +3 -0
  214. package/src/lib/ui/context/events.tsx +1 -1
  215. package/src/lib/ui/hooks/useExportAs.ts +3 -2
  216. package/src/lib/ui/hooks/useTools.tsx +1 -1
  217. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  218. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  219. package/src/lib/ui/kbd-utils.ts +10 -3
  220. package/src/lib/ui/version.ts +3 -3
  221. package/src/lib/ui.css +19 -2
  222. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +5 -5
  223. package/src/lib/utils/export/copyAs.ts +1 -24
  224. package/src/lib/utils/export/export.ts +0 -36
  225. package/src/lib/utils/export/exportAs.ts +1 -32
  226. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +4 -4
  227. package/src/test/A11y.test.tsx +3 -2
  228. package/src/test/ClickManager.test.ts +7 -6
  229. package/src/test/Editor.test.tsx +20 -19
  230. package/src/test/EraserTool.test.ts +184 -13
  231. package/src/test/HandTool.test.ts +10 -9
  232. package/src/test/HighlightShape.test.ts +2 -1
  233. package/src/test/SelectTool.test.ts +3 -2
  234. package/src/test/TLUserPreferences.test.ts +4 -3
  235. package/src/test/TestEditor.ts +13 -15
  236. package/src/test/TldrawEditor.test.tsx +11 -10
  237. package/src/test/ZoomTool.test.ts +7 -6
  238. package/src/test/__snapshots__/drawing.test.ts.snap +2 -2
  239. package/src/test/__snapshots__/groups.test.tsx.snap +6 -6
  240. package/src/test/__snapshots__/resizing.test.ts.snap +2 -2
  241. package/src/test/arrows-megabus.test.tsx +5 -4
  242. package/src/test/bindings.test.tsx +24 -37
  243. package/src/test/bookmark-shapes.test.ts +1 -8
  244. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +23 -7
  245. package/src/test/commands/__snapshots__/packShapes.test.ts.snap +8 -8
  246. package/src/test/commands/__snapshots__/zoomToFit.test.ts.snap +2 -2
  247. package/src/test/commands/alignShapes.test.tsx +25 -24
  248. package/src/test/commands/animationSpeed.test.ts +2 -1
  249. package/src/test/commands/centerOnPoint.test.ts +3 -2
  250. package/src/test/commands/clipboard.test.ts +3 -2
  251. package/src/test/commands/createShapes.test.ts +2 -1
  252. package/src/test/commands/deleteShapes.test.ts +2 -1
  253. package/src/test/commands/distributeShapes.test.tsx +11 -10
  254. package/src/test/commands/getSvgString.test.ts +2 -1
  255. package/src/test/commands/packShapes.test.ts +5 -4
  256. package/src/test/commands/resizeShape.test.ts +2 -1
  257. package/src/test/commands/rotateShapes.test.ts +7 -6
  258. package/src/test/commands/setCamera.test.ts +4 -3
  259. package/src/test/commands/setCurrentPage.test.ts +3 -2
  260. package/src/test/commands/stackShapes.test.ts +11 -10
  261. package/src/test/commands/stretch.test.tsx +13 -12
  262. package/src/test/createDeepLink.test.tsx +2 -1
  263. package/src/test/cropping.test.ts +3 -2
  264. package/src/test/custom-clipping.test.ts +436 -0
  265. package/src/test/drawing.test.ts +2 -1
  266. package/src/test/flipShapes.test.ts +4 -3
  267. package/src/test/frames.test.ts +25 -24
  268. package/src/test/getCulledShapes.test.tsx +3 -2
  269. package/src/test/groups.test.tsx +1 -1
  270. package/src/test/handleDeepLink.test.tsx +2 -1
  271. package/src/test/maxShapes.test.ts +3 -2
  272. package/src/test/modifiers.test.ts +5 -4
  273. package/src/test/navigation.test.ts +12 -11
  274. package/src/test/panning.test.ts +2 -1
  275. package/src/test/perf/perf.test.ts +2 -1
  276. package/src/test/registerDeepLinkListener.test.tsx +10 -9
  277. package/src/test/resizing.test.ts +39 -38
  278. package/src/test/select.test.tsx +4 -3
  279. package/src/test/selection-omnibus.test.ts +11 -10
  280. package/src/test/shapeutils.test.ts +4 -3
  281. package/src/test/translating.test.ts +9 -8
  282. package/tldraw.css +27 -2
  283. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  284. package/dist-cjs/lib/ui/components/StylePanel/DoubleDropdownPicker.js.map +0 -7
  285. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +0 -7
  286. package/dist-cjs/lib/ui/components/primitives/TldrawUiButtonPicker.js.map +0 -7
  287. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
  288. package/dist-esm/lib/ui/components/StylePanel/DoubleDropdownPicker.mjs.map +0 -7
  289. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +0 -7
  290. package/dist-esm/lib/ui/components/primitives/TldrawUiButtonPicker.mjs.map +0 -7
  291. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +0 -110
@@ -26,9 +26,9 @@ var import_editor = require("@tldraw/editor");
26
26
  var import_react = require("react");
27
27
  var import_events = require("../../context/events");
28
28
  var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
29
- var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
30
29
  var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
31
30
  var import_TldrawUiInput = require("../primitives/TldrawUiInput");
31
+ var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
32
32
  function AltTextEditor({ shapeId, onClose, source }) {
33
33
  const editor = (0, import_editor.useEditor)();
34
34
  const [altText, setAltText] = (0, import_react.useState)(() => {
@@ -86,9 +86,10 @@ function AltTextEditor({ shapeId, onClose, source }) {
86
86
  }
87
87
  ),
88
88
  !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
89
- import_TldrawUiButton.TldrawUiButton,
89
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
90
90
  {
91
91
  title: msg("tool.media-alt-text-confirm"),
92
+ "data-testid": "tool.media-alt-text-confirm",
92
93
  type: "icon",
93
94
  onPointerDown: import_editor.preventDefault,
94
95
  onClick: handleConfirm,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/AltTextEditor.tsx"],
4
- "sourcesContent": ["import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface AltTextEditorProps {\n\tshapeId: TLShapeId\n\tonClose(): void\n\tsource: 'image-toolbar' | 'video-toolbar'\n}\n\n/** @public @react */\nexport function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps) {\n\tconst editor = useEditor()\n\tconst [altText, setAltText] = useState(() => {\n\t\tconst shape = editor.getShape<TLShape>(shapeId)\n\t\tif (!shape) return ''\n\t\tif (!('altText' in shape.props)) throw Error('Shape does not have altText property')\n\t\treturn shape.props.altText || ''\n\t})\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst handleValueChange = (value: string) => setAltText(value)\n\n\tconst handleComplete = () => {\n\t\ttrackEvent('set-alt-text', { source })\n\t\tconst shape = editor.getShape<TLShape & { props: { altText: string } }>(shapeId)\n\t\tif (!shape) return\n\t\teditor.updateShapes([\n\t\t\t{\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tprops: { altText },\n\t\t\t},\n\t\t])\n\t\tonClose()\n\t}\n\n\tconst handleConfirm = () => handleComplete()\n\tconst handleAltTextCancel = useCallback(() => onClose(), [onClose])\n\n\tuseEffect(() => {\n\t\tref.current?.select()\n\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape') {\n\t\t\t\tevent.stopPropagation()\n\t\t\t\thandleAltTextCancel()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [handleAltTextCancel])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tclassName=\"tlui-media__toolbar-alt-text-input\"\n\t\t\t\tdata-testid=\"media-toolbar.alt-text-input\"\n\t\t\t\tvalue={altText}\n\t\t\t\tplaceholder={msg('tool.media-alt-text-desc')}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleComplete}\n\t\t\t\tonCancel={handleAltTextCancel}\n\t\t\t\tdisabled={isReadonly}\n\t\t\t/>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttitle={msg('tool.media-alt-text-confirm')}\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\t\tonClick={handleConfirm}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"check\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t</>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEE;AAjEF,oBAA8D;AAC9D,mBAAyD;AACzD,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,cAAc,EAAE,SAAS,SAAS,OAAO,GAAuB;AAC/E,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,MAAM;AAC5C,UAAM,QAAQ,OAAO,SAAkB,OAAO;AAC9C,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,EAAE,aAAa,MAAM,OAAQ,OAAM,MAAM,sCAAsC;AACnF,WAAO,MAAM,MAAM,WAAW;AAAA,EAC/B,CAAC;AACD,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,oBAAoB,CAAC,UAAkB,WAAW,KAAK;AAE7D,QAAM,iBAAiB,MAAM;AAC5B,eAAW,gBAAgB,EAAE,OAAO,CAAC;AACrC,UAAM,QAAQ,OAAO,SAAmD,OAAO;AAC/E,QAAI,CAAC,MAAO;AACZ,WAAO,aAAa;AAAA,MACnB;AAAA,QACC,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,OAAO,EAAE,QAAQ;AAAA,MAClB;AAAA,IACD,CAAC;AACD,YAAQ;AAAA,EACT;AAEA,QAAM,gBAAgB,MAAM,eAAe;AAC3C,QAAM,0BAAsB,0BAAY,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;AAElE,8BAAU,MAAM;AACf,QAAI,SAAS,OAAO;AAEpB,aAAS,cAAc,OAAsB;AAC5C,UAAI,MAAM,QAAQ,UAAU;AAC3B,cAAM,gBAAgB;AACtB,4BAAoB;AAAA,MACrB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AAAA,IACzE;AAAA,EACD,GAAG,CAAC,mBAAmB,CAAC;AAExB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QACV,eAAY;AAAA,QACZ,OAAO;AAAA,QACP,aAAa,IAAI,0BAA0B;AAAA,QAC3C,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,UAAU;AAAA;AAAA,IACX;AAAA,IACC,CAAC,cACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAO,IAAI,6BAA6B;AAAA,QACxC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KAEF;AAEF;",
4
+ "sourcesContent": ["import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface AltTextEditorProps {\n\tshapeId: TLShapeId\n\tonClose(): void\n\tsource: 'image-toolbar' | 'video-toolbar'\n}\n\n/** @public @react */\nexport function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps) {\n\tconst editor = useEditor()\n\tconst [altText, setAltText] = useState(() => {\n\t\tconst shape = editor.getShape<TLShape>(shapeId)\n\t\tif (!shape) return ''\n\t\tif (!('altText' in shape.props)) throw Error('Shape does not have altText property')\n\t\treturn shape.props.altText || ''\n\t})\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst handleValueChange = (value: string) => setAltText(value)\n\n\tconst handleComplete = () => {\n\t\ttrackEvent('set-alt-text', { source })\n\t\tconst shape = editor.getShape<TLShape & { props: { altText: string } }>(shapeId)\n\t\tif (!shape) return\n\t\teditor.updateShapes([\n\t\t\t{\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tprops: { altText },\n\t\t\t},\n\t\t])\n\t\tonClose()\n\t}\n\n\tconst handleConfirm = () => handleComplete()\n\tconst handleAltTextCancel = useCallback(() => onClose(), [onClose])\n\n\tuseEffect(() => {\n\t\tref.current?.select()\n\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape') {\n\t\t\t\tevent.stopPropagation()\n\t\t\t\thandleAltTextCancel()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [handleAltTextCancel])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tclassName=\"tlui-media__toolbar-alt-text-input\"\n\t\t\t\tdata-testid=\"media-toolbar.alt-text-input\"\n\t\t\t\tvalue={altText}\n\t\t\t\tplaceholder={msg('tool.media-alt-text-desc')}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleComplete}\n\t\t\t\tonCancel={handleAltTextCancel}\n\t\t\t\tdisabled={isReadonly}\n\t\t\t/>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttitle={msg('tool.media-alt-text-confirm')}\n\t\t\t\t\tdata-testid=\"tool.media-alt-text-confirm\"\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\t\tonClick={handleConfirm}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"check\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)}\n\t\t</>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEE;AAjEF,oBAA8D;AAC9D,mBAAyD;AACzD,oBAA4B;AAC5B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAC9B,6BAAsC;AAU/B,SAAS,cAAc,EAAE,SAAS,SAAS,OAAO,GAAuB;AAC/E,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,MAAM;AAC5C,UAAM,QAAQ,OAAO,SAAkB,OAAO;AAC9C,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,EAAE,aAAa,MAAM,OAAQ,OAAM,MAAM,sCAAsC;AACnF,WAAO,MAAM,MAAM,WAAW;AAAA,EAC/B,CAAC;AACD,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,oBAAoB,CAAC,UAAkB,WAAW,KAAK;AAE7D,QAAM,iBAAiB,MAAM;AAC5B,eAAW,gBAAgB,EAAE,OAAO,CAAC;AACrC,UAAM,QAAQ,OAAO,SAAmD,OAAO;AAC/E,QAAI,CAAC,MAAO;AACZ,WAAO,aAAa;AAAA,MACnB;AAAA,QACC,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,OAAO,EAAE,QAAQ;AAAA,MAClB;AAAA,IACD,CAAC;AACD,YAAQ;AAAA,EACT;AAEA,QAAM,gBAAgB,MAAM,eAAe;AAC3C,QAAM,0BAAsB,0BAAY,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;AAElE,8BAAU,MAAM;AACf,QAAI,SAAS,OAAO;AAEpB,aAAS,cAAc,OAAsB;AAC5C,UAAI,MAAM,QAAQ,UAAU;AAC3B,cAAM,gBAAgB;AACtB,4BAAoB;AAAA,MACrB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AAAA,IACzE;AAAA,EACD,GAAG,CAAC,mBAAmB,CAAC;AAExB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QACV,eAAY;AAAA,QACZ,OAAO;AAAA,QACP,aAAa,IAAI,0BAA0B;AAAA,QAC3C,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,UAAU;AAAA;AAAA,IACX;AAAA,IACC,CAAC,cACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAO,IAAI,6BAA6B;AAAA,QACxC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KAEF;AAEF;",
6
6
  "names": []
7
7
  }
@@ -28,11 +28,11 @@ var import_crop = require("../../../shapes/shared/crop");
28
28
  var import_actions = require("../../context/actions");
29
29
  var import_events = require("../../context/events");
30
30
  var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
31
- var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
32
31
  var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
33
32
  var import_TldrawUiButtonLabel = require("../primitives/Button/TldrawUiButtonLabel");
34
33
  var import_TldrawUiDropdownMenu = require("../primitives/TldrawUiDropdownMenu");
35
34
  var import_TldrawUiSlider = require("../primitives/TldrawUiSlider");
35
+ var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
36
36
  const DefaultImageToolbarContent = (0, import_editor.track)(function DefaultImageToolbarContent2({
37
37
  imageShapeId,
38
38
  isManipulating,
@@ -184,7 +184,15 @@ const DefaultImageToolbarContent = (0, import_editor.track)(function DefaultImag
184
184
  }
185
185
  ),
186
186
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_TldrawUiDropdownMenu.TldrawUiDropdownMenuRoot, { id: "image-toolbar-aspect-ratio", children: [
187
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiDropdownMenu.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButton.TldrawUiButton, { title: msg("tool.aspect-ratio"), type: "icon", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { icon: "corners" }) }) }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiDropdownMenu.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
188
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
189
+ {
190
+ title: msg("tool.aspect-ratio"),
191
+ type: "icon",
192
+ "data-testid": "tool.image-aspect-ratio",
193
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { icon: "corners" })
194
+ }
195
+ ) }),
188
196
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiDropdownMenu.TldrawUiDropdownMenuContent, { side: "top", align: "center", children: import_crop.ASPECT_RATIO_OPTIONS.map((aspectRatio) => {
189
197
  let checked = false;
190
198
  if (isOriginalCrop) {
@@ -215,34 +223,55 @@ const DefaultImageToolbarContent = (0, import_editor.track)(function DefaultImag
215
223
  }) })
216
224
  ] }),
217
225
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
- import_TldrawUiButton.TldrawUiButton,
226
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
219
227
  {
220
228
  type: "icon",
221
229
  onClick: onManipulatingEnd,
222
- "data-testid": "tool.image-confirm",
230
+ "data-testid": "tool.image-crop-confirm",
223
231
  style: { borderLeft: "1px solid var(--tl-color-divider)", marginLeft: "2px" },
232
+ title: msg("tool.image-crop-confirm"),
224
233
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "check" })
225
234
  }
226
235
  )
227
236
  ] });
228
237
  }
229
238
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
230
- !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButton.TldrawUiButton, { type: "icon", title: msg("tool.replace-media"), onClick: handleImageReplace, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "tool-media" }) }),
231
- !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButton.TldrawUiButton, { type: "icon", title: msg("tool.image-crop"), onClick: onManipulatingStart, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "crop" }) }),
239
+ !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
240
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
241
+ {
242
+ type: "icon",
243
+ "data-testid": "tool.image-replace",
244
+ onClick: handleImageReplace,
245
+ title: msg("tool.replace-media"),
246
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "tool-media" })
247
+ }
248
+ ),
249
+ !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
250
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
251
+ {
252
+ type: "icon",
253
+ title: msg("tool.image-crop"),
254
+ onClick: onManipulatingStart,
255
+ "data-testid": "tool.image-crop",
256
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "crop" })
257
+ }
258
+ ),
232
259
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
233
- import_TldrawUiButton.TldrawUiButton,
260
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
234
261
  {
235
262
  type: "icon",
236
263
  title: msg("action.download-original"),
237
264
  onClick: handleImageDownload,
265
+ "data-testid": "tool.image-download",
238
266
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "download" })
239
267
  }
240
268
  ),
241
269
  (altText || !isReadonly) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
242
- import_TldrawUiButton.TldrawUiButton,
270
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
243
271
  {
244
- type: "normal",
272
+ type: "icon",
245
273
  title: msg("tool.media-alt-text"),
274
+ "data-testid": "tool.image-alt-text",
246
275
  onClick: () => {
247
276
  trackEvent("alt-text-start", { source });
248
277
  onEditAltTextStart();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx"],
4
- "sourcesContent": ["import {\n\tapproximately,\n\tisEqual,\n\tkickoutOccludedShapes,\n\tmodulate,\n\tTLImageShape,\n\tTLShapePartial,\n\ttrack,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n\tASPECT_RATIO_OPTION,\n\tASPECT_RATIO_OPTIONS,\n\tASPECT_RATIO_TO_VALUE,\n\tgetCroppedImageDataForAspectRatio,\n\tgetCroppedImageDataWhenZooming,\n\tgetDefaultCrop,\n\tMAX_ZOOM,\n} from '../../../shapes/shared/crop'\nimport { useActions } from '../../context/actions'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'\nimport {\n\tTldrawUiDropdownMenuCheckboxItem,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n} from '../primitives/TldrawUiDropdownMenu'\nimport { TldrawUiSlider } from '../primitives/TldrawUiSlider'\n\n/** @public */\nexport interface DefaultImageToolbarContentProps {\n\timageShapeId: TLImageShape['id']\n\tisManipulating: boolean\n\tonEditAltTextStart(): void\n\tonManipulatingStart(): void\n\tonManipulatingEnd(): void\n}\n\n/** @public @react */\nexport const DefaultImageToolbarContent = track(function DefaultImageToolbarContent({\n\timageShapeId,\n\tisManipulating,\n\tonEditAltTextStart,\n\tonManipulatingStart,\n\tonManipulatingEnd,\n}: DefaultImageToolbarContentProps) {\n\tconst editor = useEditor()\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'image-toolbar'\n\tconst sliderRef = useRef<HTMLDivElement>(null)\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst crop = useValue('crop', () => editor.getShape<TLImageShape>(imageShapeId)!.props.crop, [\n\t\teditor,\n\t\timageShapeId,\n\t])\n\tconst zoom = crop\n\t\t? Math.min(1 - (crop.bottomRight.x - crop.topLeft.x), 1 - (crop.bottomRight.y - crop.topLeft.y))\n\t\t: 0\n\tconst [maxZoom, setMaxZoom] = useState<number | undefined>(\n\t\tcrop ? Math.max(zoom, 1 - 1 / MAX_ZOOM) : MAX_ZOOM\n\t)\n\tconst actions = useActions()\n\n\t// So, we set a maxZoom here in case there's been a manual crop applied.\n\t// Typically, you can zoom 3x into the image size (MAX_ZOOM's value).\n\t// If you go deeper than that zoom level, we need to set that as the new 100%\n\t// value on the zoom slider (otherwise you could zoom into infinity).\n\t// This balances usage of the zoom slider with manual cropping.\n\tuseEffect(() => {\n\t\tsetMaxZoom(crop ? Math.max(zoom, 1 - 1 / MAX_ZOOM) : MAX_ZOOM)\n\t}, [crop, zoom, maxZoom])\n\n\tconst onHistoryMark = useCallback((id: string) => editor.markHistoryStoppingPoint(id), [editor])\n\n\t// Apply an easing function to smooth out the zoom curve,\n\t// otherwise the zoom slider has a cubic drag feel to it which feels off.\n\tconst easeZoom = useCallback((value: number, maxValue: number): number => {\n\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\t// Use an easing function for a more natural zoom feel\n\t\treturn Math.pow(value / maxValue, maxRatioConversion) * maxValue\n\t}, [])\n\n\tconst displayValue =\n\t\tcrop && maxZoom\n\t\t\t? modulate(easeZoom(zoom, maxZoom), [0, maxZoom], [0, 100], true /* clamp */)\n\t\t\t: 0\n\n\tconst handleZoomChange = useCallback(\n\t\t(value: number) => {\n\t\t\teditor.setCurrentTool('select.crop.idle')\n\t\t\t// Convert the eased slider value back to the actual zoom value\n\t\t\tconst sliderPercent = value / 100\n\n\t\t\t// Convert the slider position back into the \"zoom\" value expected by\n\t\t\t// getCroppedImageDataWhenZooming.\n\t\t\t// 1. Undo the easing: z_out = sliderPercent^(1/maxRatioConversion) * maxZoom\n\t\t\t// 2. Translate z_out into the function's input domain. The helper computes\n\t\t\t// the *resulting* zoom (z_out) using:\n\t\t\t// z_out = 2 * z_in / (1 + 2 * z_in)\n\t\t\t// Solving for z_in gives:\n\t\t\t// z_in = z_out / (2 * (1 - z_out))\n\t\t\tconst maxDimension = 1 - 1 / MAX_ZOOM\n\t\t\tconst clampedMaxZoom = Math.min(maxDimension, maxZoom ?? maxDimension)\n\t\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\t\tconst zOut = Math.pow(sliderPercent, 1 / maxRatioConversion) * clampedMaxZoom\n\t\t\tconst zoom = zOut >= 1 ? 1 : zOut / (2 * (1 - zOut))\n\t\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)\n\t\t\tif (!imageShape) return\n\n\t\t\tconst change = getCroppedImageDataWhenZooming(zoom, imageShape, maxZoom)\n\n\t\t\teditor.updateShape({\n\t\t\t\tid: imageShape.id,\n\t\t\t\ttype: imageShape.type,\n\t\t\t\tx: change.x,\n\t\t\t\ty: change.y,\n\t\t\t\tprops: {\n\t\t\t\t\tw: change.w,\n\t\t\t\t\th: change.h,\n\t\t\t\t\tcrop: change.crop,\n\t\t\t\t},\n\t\t\t} as TLShapePartial)\n\n\t\t\ttrackEvent('set-style', { source: 'image-toolbar', id: 'zoom', value })\n\t\t},\n\t\t[editor, trackEvent, imageShapeId, maxZoom]\n\t)\n\n\tconst handleImageReplace = useCallback(\n\t\t() => actions['image-replace'].onSelect('image-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleImageDownload = useCallback(\n\t\t() => actions['download-original'].onSelect('image-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleAspectRatioChange = (aspectRatio: ASPECT_RATIO_OPTION) => {\n\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)\n\t\tif (!imageShape) return\n\t\teditor.run(() => {\n\t\t\teditor.setCurrentTool('select.crop.idle')\n\t\t\tconst change = getCroppedImageDataForAspectRatio(aspectRatio, imageShape)\n\t\t\teditor.markHistoryStoppingPoint('aspect ratio')\n\t\t\teditor.updateShape({\n\t\t\t\tid: imageShapeId,\n\t\t\t\ttype: 'image',\n\t\t\t\tx: change.x,\n\t\t\t\ty: change.y,\n\t\t\t\tprops: {\n\t\t\t\t\tcrop: change.crop,\n\t\t\t\t\tw: change.w,\n\t\t\t\t\th: change.h,\n\t\t\t\t},\n\t\t\t} as TLShapePartial)\n\t\t\tkickoutOccludedShapes(editor, [imageShapeId])\n\t\t})\n\t}\n\n\tconst altText = useValue(\n\t\t'altText',\n\t\t() => editor.getShape<TLImageShape>(imageShapeId)!.props.altText,\n\t\t[editor, imageShapeId]\n\t)\n\tconst shapeAspectRatio = useValue(\n\t\t'shapeAspectRatio',\n\t\t() => {\n\t\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)!\n\t\t\treturn imageShape.props.w / imageShape.props.h\n\t\t},\n\t\t[editor, imageShapeId]\n\t)\n\tconst isOriginalCrop = !crop || isEqual(crop, getDefaultCrop())\n\n\tuseEffect(() => {\n\t\tif (isManipulating) {\n\t\t\teditor.timers.setTimeout(() => sliderRef.current?.focus(), 0)\n\t\t}\n\t}, [editor, isManipulating])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(e: KeyboardEvent) {\n\t\t\tif (isManipulating) {\n\t\t\t\tif (e.key === 'Escape') {\n\t\t\t\t\teditor.cancel()\n\t\t\t\t\tonManipulatingEnd()\n\t\t\t\t} else if (e.key === 'Enter') {\n\t\t\t\t\teditor.complete()\n\t\t\t\t\tonManipulatingEnd()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst elm = sliderRef.current\n\t\tif (elm) {\n\t\t\telm.addEventListener('keydown', handleKeyDown)\n\t\t}\n\t\treturn () => {\n\t\t\tif (elm) {\n\t\t\t\telm.removeEventListener('keydown', handleKeyDown)\n\t\t\t}\n\t\t}\n\t}, [editor, isManipulating, onManipulatingEnd])\n\n\tif (isManipulating) {\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<TldrawUiSlider\n\t\t\t\t\tref={sliderRef}\n\t\t\t\t\tvalue={displayValue}\n\t\t\t\t\tlabel=\"tool.image-zoom\"\n\t\t\t\t\tonValueChange={handleZoomChange}\n\t\t\t\t\tonHistoryMark={onHistoryMark}\n\t\t\t\t\tmin={0}\n\t\t\t\t\tsteps={100}\n\t\t\t\t\tdata-testid=\"tool.image-zoom\"\n\t\t\t\t\ttitle={msg('tool.image-zoom')}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiDropdownMenuRoot id=\"image-toolbar-aspect-ratio\">\n\t\t\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t\t\t<TldrawUiButton title={msg('tool.aspect-ratio')} type=\"icon\">\n\t\t\t\t\t\t\t<TldrawUiButtonIcon icon=\"corners\" />\n\t\t\t\t\t\t</TldrawUiButton>\n\t\t\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t\t\t<TldrawUiDropdownMenuContent side=\"top\" align=\"center\">\n\t\t\t\t\t\t{ASPECT_RATIO_OPTIONS.map((aspectRatio) => {\n\t\t\t\t\t\t\tlet checked = false\n\t\t\t\t\t\t\tif (isOriginalCrop) {\n\t\t\t\t\t\t\t\tif (aspectRatio === 'original') {\n\t\t\t\t\t\t\t\t\tchecked = true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (aspectRatio === 'circle') {\n\t\t\t\t\t\t\t\t\tchecked = !!crop.isCircle\n\t\t\t\t\t\t\t\t} else if (aspectRatio === 'square') {\n\t\t\t\t\t\t\t\t\tchecked =\n\t\t\t\t\t\t\t\t\t\t!crop?.isCircle &&\n\t\t\t\t\t\t\t\t\t\tapproximately(shapeAspectRatio, ASPECT_RATIO_TO_VALUE[aspectRatio], 0.1)\n\t\t\t\t\t\t\t\t} else if (aspectRatio === 'original') {\n\t\t\t\t\t\t\t\t\tchecked = false\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tchecked =\n\t\t\t\t\t\t\t\t\t\t!isOriginalCrop &&\n\t\t\t\t\t\t\t\t\t\tapproximately(shapeAspectRatio, ASPECT_RATIO_TO_VALUE[aspectRatio], 0.01)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<TldrawUiDropdownMenuCheckboxItem\n\t\t\t\t\t\t\t\t\tkey={aspectRatio}\n\t\t\t\t\t\t\t\t\tonSelect={() => handleAspectRatioChange(aspectRatio as ASPECT_RATIO_OPTION)}\n\t\t\t\t\t\t\t\t\tchecked={checked}\n\t\t\t\t\t\t\t\t\ttitle={msg(`tool.aspect-ratio.${aspectRatio}`)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiButtonLabel>\n\t\t\t\t\t\t\t\t\t\t{msg(`tool.aspect-ratio.${aspectRatio}`)}\n\t\t\t\t\t\t\t\t\t</TldrawUiButtonLabel>\n\t\t\t\t\t\t\t\t</TldrawUiDropdownMenuCheckboxItem>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</TldrawUiDropdownMenuContent>\n\t\t\t\t</TldrawUiDropdownMenuRoot>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tonClick={onManipulatingEnd}\n\t\t\t\t\tdata-testid=\"tool.image-confirm\"\n\t\t\t\t\tstyle={{ borderLeft: '1px solid var(--tl-color-divider)', marginLeft: '2px' }}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"check\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t</>\n\t\t)\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton type=\"icon\" title={msg('tool.replace-media')} onClick={handleImageReplace}>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"tool-media\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton type=\"icon\" title={msg('tool.image-crop')} onClick={onManipulatingStart}>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"crop\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t\t<TldrawUiButton\n\t\t\t\ttype=\"icon\"\n\t\t\t\ttitle={msg('action.download-original')}\n\t\t\t\tonClick={handleImageDownload}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"download\" />\n\t\t\t</TldrawUiButton>\n\t\t\t{(altText || !isReadonly) && (\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"normal\"\n\t\t\t\t\ttitle={msg('tool.media-alt-text')}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\ttrackEvent('alt-text-start', { source })\n\t\t\t\t\t\tonEditAltTextStart()\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"alt\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t</>\n\t)\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tapproximately,\n\tisEqual,\n\tkickoutOccludedShapes,\n\tmodulate,\n\tTLImageShape,\n\tTLShapePartial,\n\ttrack,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n\tASPECT_RATIO_OPTION,\n\tASPECT_RATIO_OPTIONS,\n\tASPECT_RATIO_TO_VALUE,\n\tgetCroppedImageDataForAspectRatio,\n\tgetCroppedImageDataWhenZooming,\n\tgetDefaultCrop,\n\tMAX_ZOOM,\n} from '../../../shapes/shared/crop'\nimport { useActions } from '../../context/actions'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'\nimport {\n\tTldrawUiDropdownMenuCheckboxItem,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n} from '../primitives/TldrawUiDropdownMenu'\nimport { TldrawUiSlider } from '../primitives/TldrawUiSlider'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface DefaultImageToolbarContentProps {\n\timageShapeId: TLImageShape['id']\n\tisManipulating: boolean\n\tonEditAltTextStart(): void\n\tonManipulatingStart(): void\n\tonManipulatingEnd(): void\n}\n\n/** @public @react */\nexport const DefaultImageToolbarContent = track(function DefaultImageToolbarContent({\n\timageShapeId,\n\tisManipulating,\n\tonEditAltTextStart,\n\tonManipulatingStart,\n\tonManipulatingEnd,\n}: DefaultImageToolbarContentProps) {\n\tconst editor = useEditor()\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'image-toolbar'\n\tconst sliderRef = useRef<HTMLDivElement>(null)\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst crop = useValue('crop', () => editor.getShape<TLImageShape>(imageShapeId)!.props.crop, [\n\t\teditor,\n\t\timageShapeId,\n\t])\n\tconst zoom = crop\n\t\t? Math.min(1 - (crop.bottomRight.x - crop.topLeft.x), 1 - (crop.bottomRight.y - crop.topLeft.y))\n\t\t: 0\n\tconst [maxZoom, setMaxZoom] = useState<number | undefined>(\n\t\tcrop ? Math.max(zoom, 1 - 1 / MAX_ZOOM) : MAX_ZOOM\n\t)\n\tconst actions = useActions()\n\n\t// So, we set a maxZoom here in case there's been a manual crop applied.\n\t// Typically, you can zoom 3x into the image size (MAX_ZOOM's value).\n\t// If you go deeper than that zoom level, we need to set that as the new 100%\n\t// value on the zoom slider (otherwise you could zoom into infinity).\n\t// This balances usage of the zoom slider with manual cropping.\n\tuseEffect(() => {\n\t\tsetMaxZoom(crop ? Math.max(zoom, 1 - 1 / MAX_ZOOM) : MAX_ZOOM)\n\t}, [crop, zoom, maxZoom])\n\n\tconst onHistoryMark = useCallback((id: string) => editor.markHistoryStoppingPoint(id), [editor])\n\n\t// Apply an easing function to smooth out the zoom curve,\n\t// otherwise the zoom slider has a cubic drag feel to it which feels off.\n\tconst easeZoom = useCallback((value: number, maxValue: number): number => {\n\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\t// Use an easing function for a more natural zoom feel\n\t\treturn Math.pow(value / maxValue, maxRatioConversion) * maxValue\n\t}, [])\n\n\tconst displayValue =\n\t\tcrop && maxZoom\n\t\t\t? modulate(easeZoom(zoom, maxZoom), [0, maxZoom], [0, 100], true /* clamp */)\n\t\t\t: 0\n\n\tconst handleZoomChange = useCallback(\n\t\t(value: number) => {\n\t\t\teditor.setCurrentTool('select.crop.idle')\n\t\t\t// Convert the eased slider value back to the actual zoom value\n\t\t\tconst sliderPercent = value / 100\n\n\t\t\t// Convert the slider position back into the \"zoom\" value expected by\n\t\t\t// getCroppedImageDataWhenZooming.\n\t\t\t// 1. Undo the easing: z_out = sliderPercent^(1/maxRatioConversion) * maxZoom\n\t\t\t// 2. Translate z_out into the function's input domain. The helper computes\n\t\t\t// the *resulting* zoom (z_out) using:\n\t\t\t// z_out = 2 * z_in / (1 + 2 * z_in)\n\t\t\t// Solving for z_in gives:\n\t\t\t// z_in = z_out / (2 * (1 - z_out))\n\t\t\tconst maxDimension = 1 - 1 / MAX_ZOOM\n\t\t\tconst clampedMaxZoom = Math.min(maxDimension, maxZoom ?? maxDimension)\n\t\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\t\tconst zOut = Math.pow(sliderPercent, 1 / maxRatioConversion) * clampedMaxZoom\n\t\t\tconst zoom = zOut >= 1 ? 1 : zOut / (2 * (1 - zOut))\n\t\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)\n\t\t\tif (!imageShape) return\n\n\t\t\tconst change = getCroppedImageDataWhenZooming(zoom, imageShape, maxZoom)\n\n\t\t\teditor.updateShape({\n\t\t\t\tid: imageShape.id,\n\t\t\t\ttype: imageShape.type,\n\t\t\t\tx: change.x,\n\t\t\t\ty: change.y,\n\t\t\t\tprops: {\n\t\t\t\t\tw: change.w,\n\t\t\t\t\th: change.h,\n\t\t\t\t\tcrop: change.crop,\n\t\t\t\t},\n\t\t\t} as TLShapePartial)\n\n\t\t\ttrackEvent('set-style', { source: 'image-toolbar', id: 'zoom', value })\n\t\t},\n\t\t[editor, trackEvent, imageShapeId, maxZoom]\n\t)\n\n\tconst handleImageReplace = useCallback(\n\t\t() => actions['image-replace'].onSelect('image-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleImageDownload = useCallback(\n\t\t() => actions['download-original'].onSelect('image-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleAspectRatioChange = (aspectRatio: ASPECT_RATIO_OPTION) => {\n\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)\n\t\tif (!imageShape) return\n\t\teditor.run(() => {\n\t\t\teditor.setCurrentTool('select.crop.idle')\n\t\t\tconst change = getCroppedImageDataForAspectRatio(aspectRatio, imageShape)\n\t\t\teditor.markHistoryStoppingPoint('aspect ratio')\n\t\t\teditor.updateShape({\n\t\t\t\tid: imageShapeId,\n\t\t\t\ttype: 'image',\n\t\t\t\tx: change.x,\n\t\t\t\ty: change.y,\n\t\t\t\tprops: {\n\t\t\t\t\tcrop: change.crop,\n\t\t\t\t\tw: change.w,\n\t\t\t\t\th: change.h,\n\t\t\t\t},\n\t\t\t} as TLShapePartial)\n\t\t\tkickoutOccludedShapes(editor, [imageShapeId])\n\t\t})\n\t}\n\n\tconst altText = useValue(\n\t\t'altText',\n\t\t() => editor.getShape<TLImageShape>(imageShapeId)!.props.altText,\n\t\t[editor, imageShapeId]\n\t)\n\tconst shapeAspectRatio = useValue(\n\t\t'shapeAspectRatio',\n\t\t() => {\n\t\t\tconst imageShape = editor.getShape<TLImageShape>(imageShapeId)!\n\t\t\treturn imageShape.props.w / imageShape.props.h\n\t\t},\n\t\t[editor, imageShapeId]\n\t)\n\tconst isOriginalCrop = !crop || isEqual(crop, getDefaultCrop())\n\n\tuseEffect(() => {\n\t\tif (isManipulating) {\n\t\t\teditor.timers.setTimeout(() => sliderRef.current?.focus(), 0)\n\t\t}\n\t}, [editor, isManipulating])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(e: KeyboardEvent) {\n\t\t\tif (isManipulating) {\n\t\t\t\tif (e.key === 'Escape') {\n\t\t\t\t\teditor.cancel()\n\t\t\t\t\tonManipulatingEnd()\n\t\t\t\t} else if (e.key === 'Enter') {\n\t\t\t\t\teditor.complete()\n\t\t\t\t\tonManipulatingEnd()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst elm = sliderRef.current\n\t\tif (elm) {\n\t\t\telm.addEventListener('keydown', handleKeyDown)\n\t\t}\n\t\treturn () => {\n\t\t\tif (elm) {\n\t\t\t\telm.removeEventListener('keydown', handleKeyDown)\n\t\t\t}\n\t\t}\n\t}, [editor, isManipulating, onManipulatingEnd])\n\n\tif (isManipulating) {\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<TldrawUiSlider\n\t\t\t\t\tref={sliderRef}\n\t\t\t\t\tvalue={displayValue}\n\t\t\t\t\tlabel=\"tool.image-zoom\"\n\t\t\t\t\tonValueChange={handleZoomChange}\n\t\t\t\t\tonHistoryMark={onHistoryMark}\n\t\t\t\t\tmin={0}\n\t\t\t\t\tsteps={100}\n\t\t\t\t\tdata-testid=\"tool.image-zoom\"\n\t\t\t\t\ttitle={msg('tool.image-zoom')}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiDropdownMenuRoot id=\"image-toolbar-aspect-ratio\">\n\t\t\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\t\t\ttitle={msg('tool.aspect-ratio')}\n\t\t\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\t\t\tdata-testid=\"tool.image-aspect-ratio\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TldrawUiButtonIcon icon=\"corners\" />\n\t\t\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t\t\t<TldrawUiDropdownMenuContent side=\"top\" align=\"center\">\n\t\t\t\t\t\t{ASPECT_RATIO_OPTIONS.map((aspectRatio) => {\n\t\t\t\t\t\t\tlet checked = false\n\t\t\t\t\t\t\tif (isOriginalCrop) {\n\t\t\t\t\t\t\t\tif (aspectRatio === 'original') {\n\t\t\t\t\t\t\t\t\tchecked = true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (aspectRatio === 'circle') {\n\t\t\t\t\t\t\t\t\tchecked = !!crop.isCircle\n\t\t\t\t\t\t\t\t} else if (aspectRatio === 'square') {\n\t\t\t\t\t\t\t\t\tchecked =\n\t\t\t\t\t\t\t\t\t\t!crop?.isCircle &&\n\t\t\t\t\t\t\t\t\t\tapproximately(shapeAspectRatio, ASPECT_RATIO_TO_VALUE[aspectRatio], 0.1)\n\t\t\t\t\t\t\t\t} else if (aspectRatio === 'original') {\n\t\t\t\t\t\t\t\t\tchecked = false\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tchecked =\n\t\t\t\t\t\t\t\t\t\t!isOriginalCrop &&\n\t\t\t\t\t\t\t\t\t\tapproximately(shapeAspectRatio, ASPECT_RATIO_TO_VALUE[aspectRatio], 0.01)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<TldrawUiDropdownMenuCheckboxItem\n\t\t\t\t\t\t\t\t\tkey={aspectRatio}\n\t\t\t\t\t\t\t\t\tonSelect={() => handleAspectRatioChange(aspectRatio as ASPECT_RATIO_OPTION)}\n\t\t\t\t\t\t\t\t\tchecked={checked}\n\t\t\t\t\t\t\t\t\ttitle={msg(`tool.aspect-ratio.${aspectRatio}`)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiButtonLabel>\n\t\t\t\t\t\t\t\t\t\t{msg(`tool.aspect-ratio.${aspectRatio}`)}\n\t\t\t\t\t\t\t\t\t</TldrawUiButtonLabel>\n\t\t\t\t\t\t\t\t</TldrawUiDropdownMenuCheckboxItem>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</TldrawUiDropdownMenuContent>\n\t\t\t\t</TldrawUiDropdownMenuRoot>\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tonClick={onManipulatingEnd}\n\t\t\t\t\tdata-testid=\"tool.image-crop-confirm\"\n\t\t\t\t\tstyle={{ borderLeft: '1px solid var(--tl-color-divider)', marginLeft: '2px' }}\n\t\t\t\t\ttitle={msg('tool.image-crop-confirm')}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"check\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t</>\n\t\t)\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tdata-testid=\"tool.image-replace\"\n\t\t\t\t\tonClick={handleImageReplace}\n\t\t\t\t\ttitle={msg('tool.replace-media')}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"tool-media\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)}\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttitle={msg('tool.image-crop')}\n\t\t\t\t\tonClick={onManipulatingStart}\n\t\t\t\t\tdata-testid=\"tool.image-crop\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"crop\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)}\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\ttype=\"icon\"\n\t\t\t\ttitle={msg('action.download-original')}\n\t\t\t\tonClick={handleImageDownload}\n\t\t\t\tdata-testid=\"tool.image-download\"\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"download\" />\n\t\t\t</TldrawUiToolbarButton>\n\t\t\t{(altText || !isReadonly) && (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttitle={msg('tool.media-alt-text')}\n\t\t\t\t\tdata-testid=\"tool.image-alt-text\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\ttrackEvent('alt-text-start', { source })\n\t\t\t\t\t\tonEditAltTextStart()\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"alt\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)}\n\t\t</>\n\t)\n})\n"],
5
+ "mappings": "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",
6
6
  "names": ["DefaultImageToolbarContent", "zoom"]
7
7
  }
@@ -29,6 +29,7 @@ var import_events = require("../../context/events");
29
29
  var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
30
30
  var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
31
31
  var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
32
+ var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
32
33
  const DefaultVideoToolbarContent = (0, import_editor.track)(function DefaultVideoToolbarContent2({
33
34
  videoShapeId,
34
35
  onEditAltTextStart
@@ -53,22 +54,33 @@ const DefaultVideoToolbarContent = (0, import_editor.track)(function DefaultVide
53
54
  [editor, videoShapeId]
54
55
  );
55
56
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
56
- !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButton.TldrawUiButton, { type: "icon", title: msg("tool.replace-media"), onClick: handleVideoReplace, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "tool-media" }) }),
57
+ !isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
58
+ import_TldrawUiButton.TldrawUiButton,
59
+ {
60
+ type: "icon",
61
+ title: msg("tool.replace-media"),
62
+ onClick: handleVideoReplace,
63
+ "data-testid": "tool.video-replace",
64
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "tool-media" })
65
+ }
66
+ ),
57
67
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
58
68
  import_TldrawUiButton.TldrawUiButton,
59
69
  {
60
70
  type: "icon",
61
71
  title: msg("action.download-original"),
62
72
  onClick: handleVideoDownload,
73
+ "data-testid": "tool.video-download",
63
74
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiButtonIcon.TldrawUiButtonIcon, { small: true, icon: "download" })
64
75
  }
65
76
  ),
66
77
  (altText || !isReadonly) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
- import_TldrawUiButton.TldrawUiButton,
78
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
68
79
  {
69
- type: "normal",
80
+ type: "icon",
70
81
  isActive: !!altText,
71
82
  title: msg("tool.media-alt-text"),
83
+ "data-testid": "tool.video-alt-text",
72
84
  onClick: () => {
73
85
  trackEvent("alt-text-start", { source });
74
86
  onEditAltTextStart();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx"],
4
- "sourcesContent": ["import { TLVideoShape, track, useEditor, useValue } from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport { useActions } from '../../context/actions'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\n\n/** @public */\nexport interface DefaultVideoToolbarContentProps {\n\tvideoShapeId: TLVideoShape['id']\n\tonEditAltTextStart(): void\n}\n\n/** @public @react */\nexport const DefaultVideoToolbarContent = track(function DefaultVideoToolbarContent({\n\tvideoShapeId,\n\tonEditAltTextStart,\n}: DefaultVideoToolbarContentProps) {\n\tconst editor = useEditor()\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'video-toolbar'\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst actions = useActions()\n\n\tconst handleVideoReplace = useCallback(\n\t\t() => actions['video-replace'].onSelect('video-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleVideoDownload = useCallback(\n\t\t() => actions['download-original'].onSelect('video-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst altText = useValue(\n\t\t'altText',\n\t\t() => editor.getShape<TLVideoShape>(videoShapeId)!.props.altText,\n\t\t[editor, videoShapeId]\n\t)\n\n\treturn (\n\t\t<>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton type=\"icon\" title={msg('tool.replace-media')} onClick={handleVideoReplace}>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"tool-media\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t\t<TldrawUiButton\n\t\t\t\ttype=\"icon\"\n\t\t\t\ttitle={msg('action.download-original')}\n\t\t\t\tonClick={handleVideoDownload}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"download\" />\n\t\t\t</TldrawUiButton>\n\t\t\t{(altText || !isReadonly) && (\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"normal\"\n\t\t\t\t\tisActive={!!altText}\n\t\t\t\t\ttitle={msg('tool.media-alt-text')}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\ttrackEvent('alt-text-start', { source })\n\t\t\t\t\t\tonEditAltTextStart()\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"alt\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t</>\n\t)\n})\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CE;AA5CF,oBAAyD;AACzD,mBAA4B;AAC5B,qBAA2B;AAC3B,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AAS5B,MAAM,iCAA6B,qBAAM,SAASA,4BAA2B;AAAA,EACnF;AAAA,EACA;AACD,GAAoC;AACnC,QAAM,aAAS,yBAAU;AACzB,QAAM,iBAAa,2BAAY;AAC/B,QAAM,UAAM,sCAAe;AAC3B,QAAM,SAAS;AACf,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,cAAU,2BAAW;AAE3B,QAAM,yBAAqB;AAAA,IAC1B,MAAM,QAAQ,eAAe,EAAE,SAAS,eAAe;AAAA,IACvD,CAAC,OAAO;AAAA,EACT;AAEA,QAAM,0BAAsB;AAAA,IAC3B,MAAM,QAAQ,mBAAmB,EAAE,SAAS,eAAe;AAAA,IAC3D,CAAC,OAAO;AAAA,EACT;AAEA,QAAM,cAAU;AAAA,IACf;AAAA,IACA,MAAM,OAAO,SAAuB,YAAY,EAAG,MAAM;AAAA,IACzD,CAAC,QAAQ,YAAY;AAAA,EACtB;AAEA,SACC,4EACE;AAAA,KAAC,cACD,4CAAC,wCAAe,MAAK,QAAO,OAAO,IAAI,oBAAoB,GAAG,SAAS,oBACtE,sDAAC,gDAAmB,OAAK,MAAC,MAAK,cAAa,GAC7C;AAAA,IAED;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,OAAO,IAAI,0BAA0B;AAAA,QACrC,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,YAAW;AAAA;AAAA,IAC3C;AAAA,KACE,WAAW,CAAC,eACb;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,UAAU,CAAC,CAAC;AAAA,QACZ,OAAO,IAAI,qBAAqB;AAAA,QAChC,SAAS,MAAM;AACd,qBAAW,kBAAkB,EAAE,OAAO,CAAC;AACvC,6BAAmB;AAAA,QACpB;AAAA,QAEA,sDAAC,gDAAmB,OAAK,MAAC,MAAK,OAAM;AAAA;AAAA,IACtC;AAAA,KAEF;AAEF,CAAC;",
4
+ "sourcesContent": ["import { TLVideoShape, track, useEditor, useValue } from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport { useActions } from '../../context/actions'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface DefaultVideoToolbarContentProps {\n\tvideoShapeId: TLVideoShape['id']\n\tonEditAltTextStart(): void\n}\n\n/** @public @react */\nexport const DefaultVideoToolbarContent = track(function DefaultVideoToolbarContent({\n\tvideoShapeId,\n\tonEditAltTextStart,\n}: DefaultVideoToolbarContentProps) {\n\tconst editor = useEditor()\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'video-toolbar'\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst actions = useActions()\n\n\tconst handleVideoReplace = useCallback(\n\t\t() => actions['video-replace'].onSelect('video-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst handleVideoDownload = useCallback(\n\t\t() => actions['download-original'].onSelect('video-toolbar'),\n\t\t[actions]\n\t)\n\n\tconst altText = useValue(\n\t\t'altText',\n\t\t() => editor.getShape<TLVideoShape>(videoShapeId)!.props.altText,\n\t\t[editor, videoShapeId]\n\t)\n\n\treturn (\n\t\t<>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttitle={msg('tool.replace-media')}\n\t\t\t\t\tonClick={handleVideoReplace}\n\t\t\t\t\tdata-testid=\"tool.video-replace\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"tool-media\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t\t<TldrawUiButton\n\t\t\t\ttype=\"icon\"\n\t\t\t\ttitle={msg('action.download-original')}\n\t\t\t\tonClick={handleVideoDownload}\n\t\t\t\tdata-testid=\"tool.video-download\"\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"download\" />\n\t\t\t</TldrawUiButton>\n\t\t\t{(altText || !isReadonly) && (\n\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tisActive={!!altText}\n\t\t\t\t\ttitle={msg('tool.media-alt-text')}\n\t\t\t\t\tdata-testid=\"tool.video-alt-text\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\ttrackEvent('alt-text-start', { source })\n\t\t\t\t\t\tonEditAltTextStart()\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"alt\" />\n\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t)}\n\t\t</>\n\t)\n})\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6CE;AA7CF,oBAAyD;AACzD,mBAA4B;AAC5B,qBAA2B;AAC3B,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,6BAAsC;AAS/B,MAAM,iCAA6B,qBAAM,SAASA,4BAA2B;AAAA,EACnF;AAAA,EACA;AACD,GAAoC;AACnC,QAAM,aAAS,yBAAU;AACzB,QAAM,iBAAa,2BAAY;AAC/B,QAAM,UAAM,sCAAe;AAC3B,QAAM,SAAS;AACf,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,cAAU,2BAAW;AAE3B,QAAM,yBAAqB;AAAA,IAC1B,MAAM,QAAQ,eAAe,EAAE,SAAS,eAAe;AAAA,IACvD,CAAC,OAAO;AAAA,EACT;AAEA,QAAM,0BAAsB;AAAA,IAC3B,MAAM,QAAQ,mBAAmB,EAAE,SAAS,eAAe;AAAA,IAC3D,CAAC,OAAO;AAAA,EACT;AAEA,QAAM,cAAU;AAAA,IACf;AAAA,IACA,MAAM,OAAO,SAAuB,YAAY,EAAG,MAAM;AAAA,IACzD,CAAC,QAAQ,YAAY;AAAA,EACtB;AAEA,SACC,4EACE;AAAA,KAAC,cACD;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,OAAO,IAAI,oBAAoB;AAAA,QAC/B,SAAS;AAAA,QACT,eAAY;AAAA,QAEZ,sDAAC,gDAAmB,OAAK,MAAC,MAAK,cAAa;AAAA;AAAA,IAC7C;AAAA,IAED;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,OAAO,IAAI,0BAA0B;AAAA,QACrC,SAAS;AAAA,QACT,eAAY;AAAA,QAEZ,sDAAC,gDAAmB,OAAK,MAAC,MAAK,YAAW;AAAA;AAAA,IAC3C;AAAA,KACE,WAAW,CAAC,eACb;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,UAAU,CAAC,CAAC;AAAA,QACZ,OAAO,IAAI,qBAAqB;AAAA,QAChC,eAAY;AAAA,QACZ,SAAS,MAAM;AACd,qBAAW,kBAAkB,EAAE,OAAO,CAAC;AACvC,6BAAmB;AAAA,QACpB;AAAA,QAEA,sDAAC,gDAAmB,OAAK,MAAC,MAAK,OAAM;AAAA;AAAA,IACtC;AAAA,KAEF;AAEF,CAAC;",
6
6
  "names": ["DefaultVideoToolbarContent"]
7
7
  }
@@ -26,9 +26,9 @@ var import_editor = require("@tldraw/editor");
26
26
  var import_react = require("react");
27
27
  var import_events = require("../../context/events");
28
28
  var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
29
- var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
30
29
  var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
31
30
  var import_TldrawUiInput = require("../primitives/TldrawUiInput");
31
+ var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
32
32
  function LinkEditor({ textEditor, value: initialValue, onClose }) {
33
33
  const editor = (0, import_editor.useEditor)();
34
34
  const [value, setValue] = (0, import_react.useState)(initialValue);
@@ -83,7 +83,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
83
83
  }
84
84
  ),
85
85
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
86
- import_TldrawUiButton.TldrawUiButton,
86
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
87
87
  {
88
88
  className: "tlui-rich-text__toolbar-link-visit",
89
89
  title: msg("tool.rich-text-link-visit"),
@@ -95,7 +95,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
95
95
  }
96
96
  ),
97
97
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
98
- import_TldrawUiButton.TldrawUiButton,
98
+ import_TldrawUiToolbar.TldrawUiToolbarButton,
99
99
  {
100
100
  className: "tlui-rich-text__toolbar-link-remove",
101
101
  title: msg("tool.rich-text-link-remove"),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
4
- "sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.commands.setLink({ href: link })\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAG1C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,8BAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,8BAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA;AAAA,IACb;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,sDAAC,gDAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
4
+ "sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.commands.setLink({ href: link })\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiToolbarButton>\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiToolbarButton>\n\t\t</>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAC9B,6BAAsC;AAU/B,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAG1C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,8BAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,8BAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA;AAAA,IACb;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,sDAAC,gDAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
6
6
  "names": ["value"]
7
7
  }
@@ -139,7 +139,16 @@ const TldrawUiContextualToolbar = ({
139
139
  "data-testid": "contextual-toolbar",
140
140
  className: (0, import_classnames.default)("tlui-contextual-toolbar", className),
141
141
  onPointerDown: import_editor.stopEventPropagation,
142
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiToolbar.TldrawUiToolbar, { orientation: "horizontal", className: "tlui-menu", label, children })
142
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
143
+ import_TldrawUiToolbar.TldrawUiToolbar,
144
+ {
145
+ orientation: "horizontal",
146
+ className: "tlui-menu",
147
+ label,
148
+ tooltipSide: "top",
149
+ children
150
+ }
151
+ )
143
152
  }
144
153
  );
145
154
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx"],
4
- "sourcesContent": ["import {\n\tassert,\n\tBox,\n\tclamp,\n\tEditor,\n\treact,\n\tstopEventPropagation,\n\tuseAtom,\n\tuseEditor,\n\tusePassThroughMouseOverEvents,\n\tusePassThroughWheelEvents,\n\tuseValue,\n\tVec,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport React, { RefObject, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { flushSync } from 'react-dom'\nimport { TldrawUiToolbar } from './TldrawUiToolbar'\n\nconst MOVE_TIMEOUT = 150\nconst HIDE_VISIBILITY_TIMEOUT = 16\nconst SHOW_VISIBILITY_TIMEOUT = 16\nconst MIN_DISTANCE_TO_REPOSITION_SQUARED = 16 ** 2\nconst TOOLBAR_GAP = 8\nconst SCREEN_MARGIN = 16\nconst HIDE_TOOLBAR_WHEN_CAMERA_IS_MOVING = false\nconst LEFT_ALIGN_TOOLBAR = false\n\n/** @public */\nexport interface TLUiContextualToolbarProps {\n\tchildren?: React.ReactNode\n\tclassName?: string\n\tisMousingDown?: boolean\n\tgetSelectionBounds(): Box | undefined\n\tchangeOnlyWhenYChanges?: boolean\n\tlabel: string\n}\n\n/**\n * A generic floating toolbar that can be used for things\n * like rich text editing, image toolbars, etc.\n *\n * @public @react\n */\nexport const TldrawUiContextualToolbar = ({\n\tchildren,\n\tclassName,\n\tisMousingDown,\n\tgetSelectionBounds,\n\tchangeOnlyWhenYChanges = false,\n\tlabel,\n}: TLUiContextualToolbarProps) => {\n\tconst editor = useEditor()\n\tconst toolbarRef = useRef<HTMLDivElement>(null)\n\n\tusePassThroughWheelEvents(toolbarRef as RefObject<HTMLDivElement>)\n\tusePassThroughMouseOverEvents(toolbarRef as RefObject<HTMLDivElement>)\n\n\tconst { isVisible, isInteractive, hide, show, position, move } =\n\t\tuseToolbarVisibilityStateMachine(changeOnlyWhenYChanges)\n\n\t// annoying react stuff: we don't want the toolbar position function to depend on the react state so we'll double with a ref\n\tconst rCouldShowToolbar = useRef(false)\n\tconst [hasValidToolbarPosition, setHasValidToolbarPosition] = useState(false)\n\n\tconst contentSizeUpdateCounter = useAtom('content size update counter', 0)\n\n\tuseEffect(() => {\n\t\tassert(toolbarRef.current)\n\t\tconst observer = new ResizeObserver(() => {\n\t\t\tcontentSizeUpdateCounter.update((n) => n + 1)\n\t\t})\n\t\tobserver.observe(toolbarRef.current)\n\t\treturn () => observer.disconnect()\n\t}, [contentSizeUpdateCounter])\n\n\tuseEffect(() => {\n\t\tlet lastContentSizeUpdateCounter = contentSizeUpdateCounter.get()\n\t\treturn react('toolbar position', function updateToolbarPositionAndDisplay() {\n\t\t\tconst toolbarElm = toolbarRef.current\n\t\t\tif (!toolbarElm) return\n\n\t\t\tconst nextContentSizeUpdateCounter = contentSizeUpdateCounter.get()\n\n\t\t\t// capture / force this to update when...\n\t\t\teditor.getCamera() // the camera moves\n\t\t\tcontentSizeUpdateCounter.get() // the toolbar size changes\n\t\t\t// undefined here means that we can't show the toolbar due to an incompatible position\n\t\t\tconst position = getToolbarScreenPosition(editor, toolbarElm, getSelectionBounds)\n\n\t\t\t// todo: when the toolbar is hidden due to the selection being off screen, it should be hidden immediately\n\t\t\t// rather than waiting for the position to settle. This is different than when the position changes due to\n\t\t\t// a change in the user's selection.\n\t\t\tif (!position) {\n\t\t\t\tif (rCouldShowToolbar.current) {\n\t\t\t\t\t// If we don't have a position, then we're not showing the toolbar\n\t\t\t\t\trCouldShowToolbar.current = false\n\t\t\t\t\tsetHasValidToolbarPosition(false)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// If the camera state is moving, we want to immediately update the position\n\t\t\t\t// todo: consider hiding the toolbar while the camera is moving\n\t\t\t\tconst cameraState = editor.getCameraState()\n\t\t\t\tif (cameraState === 'moving') {\n\t\t\t\t\t// ...if we wanted this to avoid prematurely updating any positions, we'd need\n\t\t\t\t\t// to have the last updated position in page space, so that we could convert\n\t\t\t\t\t// it to screen space and update it here\n\t\t\t\t\tconst elm = toolbarRef.current\n\t\t\t\t\telm.style.setProperty('transform', `translate(${position.x}px, ${position.y}px)`)\n\t\t\t\t} else {\n\t\t\t\t\tconst moveImmediately = lastContentSizeUpdateCounter !== nextContentSizeUpdateCounter\n\t\t\t\t\t// Schedule a move to its next location\n\t\t\t\t\tmove(position.x, position.y, moveImmediately)\n\t\t\t\t}\n\n\t\t\t\t// Finally, if the toolbar was previously hidden, show it again\n\t\t\t\tif (!rCouldShowToolbar.current) {\n\t\t\t\t\trCouldShowToolbar.current = true\n\t\t\t\t\tsetHasValidToolbarPosition(true)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlastContentSizeUpdateCounter = nextContentSizeUpdateCounter\n\t\t})\n\t}, [editor, getSelectionBounds, contentSizeUpdateCounter, move])\n\n\tconst cameraState = useValue('camera state', () => editor.getCameraState(), [editor])\n\n\t// Send the hide or show events based on whether the user is clicking\n\t// and whether the toolbar's position is valid\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && HIDE_TOOLBAR_WHEN_CAMERA_IS_MOVING) {\n\t\t\thide(true)\n\t\t\treturn\n\t\t}\n\n\t\tif (isMousingDown || !hasValidToolbarPosition) {\n\t\t\thide()\n\t\t\treturn\n\t\t}\n\n\t\tshow()\n\t}, [hasValidToolbarPosition, cameraState, isMousingDown, show, hide])\n\n\t// When the visibility changes, update the toolbar's visibility\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.dataset.visible = `${isVisible}`\n\t}, [isVisible, position])\n\n\t// When the position changes, update the toolbar's position on screen\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.style.setProperty('transform', `translate(${position.x}px, ${position.y}px)`)\n\t}, [position])\n\n\t// When the interactivity changes, update the toolbar's interactivity\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.dataset.interactive = `${isInteractive}`\n\t}, [isInteractive])\n\n\treturn (\n\t\t<div\n\t\t\tref={toolbarRef}\n\t\t\tdata-interactive={false}\n\t\t\tdata-visible={false}\n\t\t\tdata-testid=\"contextual-toolbar\"\n\t\t\tclassName={classNames('tlui-contextual-toolbar', className)}\n\t\t\tonPointerDown={stopEventPropagation}\n\t\t>\n\t\t\t<TldrawUiToolbar orientation=\"horizontal\" className=\"tlui-menu\" label={label}>\n\t\t\t\t{children}\n\t\t\t</TldrawUiToolbar>\n\t\t</div>\n\t)\n}\n\n// For convenience, let's work just with boxes here\n/** @internal */\nexport function rectToBox(rect: DOMRect): Box {\n\treturn new Box(rect.x, rect.y, rect.width, rect.height)\n}\n\nexport function getToolbarScreenPosition(\n\teditor: Editor,\n\ttoolbarElm: HTMLElement,\n\tgetSelectionBounds: () => Box | undefined\n) {\n\tconst selectionBounds = getSelectionBounds()?.clone()\n\tif (!selectionBounds) return\n\n\t// Offset the selection bounds by the viewport screen bounds (if the editor is scrolled or inset, etc)\n\tconst vsb = editor.getViewportScreenBounds()\n\tselectionBounds.x -= vsb.x\n\tselectionBounds.y -= vsb.y\n\n\t// If the selection bounds are too far off of the screen, don't show the toolbar\n\tif (\n\t\tselectionBounds.midY < SCREEN_MARGIN ||\n\t\tselectionBounds.midY > vsb.h - SCREEN_MARGIN ||\n\t\tselectionBounds.midX < SCREEN_MARGIN ||\n\t\tselectionBounds.midX > vsb.w - SCREEN_MARGIN\n\t) {\n\t\treturn\n\t}\n\n\t// Get the toolbar's screen rect as a box. Do this after we verify that there is at least one selection.\n\tconst toolbarBounds = rectToBox(toolbarElm.getBoundingClientRect())\n\n\t// Chance these are NaN? Rare case.\n\tif (!toolbarBounds.width || !toolbarBounds.height) return\n\n\t// Thrashy, only do this if we're showing the toolbar\n\t// ! this might not be needed, the container never scrolls\n\tconst { scrollLeft, scrollTop } = editor.getContainer()\n\n\t// We want to position the toolbar so that it is centered over the selection\n\t// except in the cases where it would extend off the edge of the screen.\n\n\t// Start by placing the top left corner of the toolbar so that the\n\t// toolbar would be centered above the section bounds, bumped up by the\n\n\tlet x = LEFT_ALIGN_TOOLBAR ? selectionBounds.x : selectionBounds.midX - toolbarBounds.w / 2\n\tlet y = selectionBounds.y - toolbarBounds.h - TOOLBAR_GAP\n\n\t// Clamp the position on screen.\n\tx = clamp(x, SCREEN_MARGIN, vsb.w - toolbarBounds.w - SCREEN_MARGIN)\n\ty = clamp(y, SCREEN_MARGIN, vsb.h - toolbarBounds.h - SCREEN_MARGIN)\n\n\t// Offset the position by the container's scroll position\n\tx += scrollLeft\n\ty += scrollTop\n\n\t// Round the position to the nearest pixel\n\tx = Math.round(x)\n\ty = Math.round(y)\n\n\treturn { x, y }\n}\n\nfunction sufficientlyDistant(curr: Vec, next: Vec, changeOnlyWhenYChanges: boolean) {\n\tif (changeOnlyWhenYChanges) {\n\t\treturn Vec.Sub(next, curr).y ** 2 >= MIN_DISTANCE_TO_REPOSITION_SQUARED\n\t}\n\treturn Vec.Len2(Vec.Sub(next, curr)) >= MIN_DISTANCE_TO_REPOSITION_SQUARED\n}\n\nexport function useToolbarVisibilityStateMachine(changeOnlyWhenYChanges: boolean) {\n\tconst editor = useEditor()\n\n\tconst rState = useRef<\n\t\t{ name: 'hidden' } | { name: 'showing' } | { name: 'shown' } | { name: 'hiding' }\n\t>({ name: 'hidden' })\n\n\t// The toolbar should only be interactive when in the 'shown' state\n\tconst [isInteractive, setIsInteractive] = useState(false)\n\n\t// The toolbar is visible in the 'shown' and 'hiding' states\n\tconst [isVisible, setIsVisible] = useState(false)\n\n\t// The position is updated when entering the 'shown' state or when moving while in the 'shown' state\n\tconst [position, setPosition] = useState({ x: -1000, y: -1000 })\n\n\t// The toolbar's current position\n\tconst rCurrPosition = useRef(new Vec(-1000, -1000))\n\n\t// The toolbar's proposed next position\n\tconst rNextPosition = useRef(new Vec(-1000, -1000))\n\n\t// A timeout needs to be completed before the toolbar is shown or hidden\n\tconst rStableVisibilityTimeout = useRef<any>(-1)\n\n\t// A timeout needs to be completed before the toolbar's position changes moved\n\tconst rStablePositionTimeout = useRef<any>(-1)\n\n\t/**\n\t * Send the 'move' event whenever something happens that would cause the toolbar's position to change.\n\t * Any update here will cause\n\t * If the state is 'shown', it will start a new timeout that will update the toolbar's position after it completes.\n\t */\n\tconst move = useCallback(\n\t\t(x: number, y: number, immediate = false) => {\n\t\t\t// Update the next proposed position\n\t\t\trNextPosition.current.x = x\n\t\t\trNextPosition.current.y = y\n\n\t\t\t// If the toolbar is not yet visible, don't do anything\n\t\t\tif (rState.current.name === 'hidden' || rState.current.name === 'showing') return\n\n\t\t\t// If showing or hiding, cancel the position timeout and start a new one.\n\t\t\t// When the timeout ends, if we're in the 'shown' state and the position has changed sufficiently\n\t\t\t// from the last visible position, update the position.\n\t\t\tclearTimeout(rStablePositionTimeout.current)\n\n\t\t\tconst flushMove = () => {\n\t\t\t\tif (\n\t\t\t\t\trState.current.name === 'shown' &&\n\t\t\t\t\tsufficientlyDistant(rNextPosition.current, rCurrPosition.current, changeOnlyWhenYChanges)\n\t\t\t\t) {\n\t\t\t\t\tconst { x, y } = rNextPosition.current\n\t\t\t\t\trCurrPosition.current = new Vec(x, y)\n\t\t\t\t\tif (immediate) {\n\t\t\t\t\t\tflushSync(() => setPosition({ x, y }))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetPosition({ x, y })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (immediate) {\n\t\t\t\tflushMove()\n\t\t\t} else {\n\t\t\t\trStablePositionTimeout.current = editor.timers.setTimeout(flushMove, MOVE_TIMEOUT)\n\t\t\t}\n\t\t},\n\t\t[editor, changeOnlyWhenYChanges]\n\t)\n\n\t/**\n\t * Send the hide event whenever a change occurs that would cause the toolbar to become invisible.\n\t * If the state is 'shown', it will enter 'hiding' and then 'hidden' after a timeout completes.\n\t * If the state is 'showing', it will cancel the visibility timeout and enter 'hidden' immediately.\n\t */\n\tconst hide = useCallback(\n\t\t(immediate = false) => {\n\t\t\tswitch (rState.current.name) {\n\t\t\t\tcase 'showing': {\n\t\t\t\t\tclearTimeout(rStableVisibilityTimeout.current)\n\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'shown': {\n\t\t\t\t\trState.current = { name: 'hiding' }\n\t\t\t\t\tsetIsInteractive(false) // when leaving shown, turn back on interactions\n\n\t\t\t\t\tif (immediate) {\n\t\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\t\tsetIsVisible(false)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trStableVisibilityTimeout.current = editor.timers.setTimeout(() => {\n\t\t\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\t\t\tsetIsVisible(false)\n\t\t\t\t\t\t}, HIDE_VISIBILITY_TIMEOUT)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\t// noop\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\t/**\n\t * Send the show event whenever a change occurs that would cause the toolbar to become visible.\n\t * If the state is 'hidden', it will enter 'showing' and then 'shown' after a timeout completes.\n\t * If the state is 'hiding', it will cancel the visibility timeout and enter 'shown' immediately.\n\t */\n\tconst show = useCallback(() => {\n\t\tswitch (rState.current.name) {\n\t\t\tcase 'hidden': {\n\t\t\t\trState.current = { name: 'showing' }\n\t\t\t\trStableVisibilityTimeout.current = editor.timers.setTimeout(() => {\n\t\t\t\t\t// position\n\t\t\t\t\tconst { x, y } = rNextPosition.current\n\t\t\t\t\trCurrPosition.current = new Vec(x, y)\n\t\t\t\t\tsetPosition({ x, y })\n\n\t\t\t\t\trState.current = { name: 'shown' }\n\t\t\t\t\tsetIsVisible(true)\n\t\t\t\t\tsetIsInteractive(true)\n\t\t\t\t}, SHOW_VISIBILITY_TIMEOUT)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'hiding': {\n\t\t\t\t// Go back to shown immediately\n\t\t\t\tclearTimeout(rStableVisibilityTimeout.current)\n\t\t\t\trState.current = { name: 'shown' }\n\t\t\t\tsetIsInteractive(true) // when entering shown, turn back on interactions\n\t\t\t\tmove(rNextPosition.current.x, rNextPosition.current.y)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// noop\n\t\t\t}\n\t\t}\n\t}, [editor, move])\n\n\treturn { isVisible, isInteractive, show, hide, move, position }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tassert,\n\tBox,\n\tclamp,\n\tEditor,\n\treact,\n\tstopEventPropagation,\n\tuseAtom,\n\tuseEditor,\n\tusePassThroughMouseOverEvents,\n\tusePassThroughWheelEvents,\n\tuseValue,\n\tVec,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport React, { RefObject, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { flushSync } from 'react-dom'\nimport { TldrawUiToolbar } from './TldrawUiToolbar'\n\nconst MOVE_TIMEOUT = 150\nconst HIDE_VISIBILITY_TIMEOUT = 16\nconst SHOW_VISIBILITY_TIMEOUT = 16\nconst MIN_DISTANCE_TO_REPOSITION_SQUARED = 16 ** 2\nconst TOOLBAR_GAP = 8\nconst SCREEN_MARGIN = 16\nconst HIDE_TOOLBAR_WHEN_CAMERA_IS_MOVING = false\nconst LEFT_ALIGN_TOOLBAR = false\n\n/** @public */\nexport interface TLUiContextualToolbarProps {\n\tchildren?: React.ReactNode\n\tclassName?: string\n\tisMousingDown?: boolean\n\tgetSelectionBounds(): Box | undefined\n\tchangeOnlyWhenYChanges?: boolean\n\tlabel: string\n}\n\n/**\n * A generic floating toolbar that can be used for things\n * like rich text editing, image toolbars, etc.\n *\n * @public @react\n */\nexport const TldrawUiContextualToolbar = ({\n\tchildren,\n\tclassName,\n\tisMousingDown,\n\tgetSelectionBounds,\n\tchangeOnlyWhenYChanges = false,\n\tlabel,\n}: TLUiContextualToolbarProps) => {\n\tconst editor = useEditor()\n\tconst toolbarRef = useRef<HTMLDivElement>(null)\n\n\tusePassThroughWheelEvents(toolbarRef as RefObject<HTMLDivElement>)\n\tusePassThroughMouseOverEvents(toolbarRef as RefObject<HTMLDivElement>)\n\n\tconst { isVisible, isInteractive, hide, show, position, move } =\n\t\tuseToolbarVisibilityStateMachine(changeOnlyWhenYChanges)\n\n\t// annoying react stuff: we don't want the toolbar position function to depend on the react state so we'll double with a ref\n\tconst rCouldShowToolbar = useRef(false)\n\tconst [hasValidToolbarPosition, setHasValidToolbarPosition] = useState(false)\n\n\tconst contentSizeUpdateCounter = useAtom('content size update counter', 0)\n\n\tuseEffect(() => {\n\t\tassert(toolbarRef.current)\n\t\tconst observer = new ResizeObserver(() => {\n\t\t\tcontentSizeUpdateCounter.update((n) => n + 1)\n\t\t})\n\t\tobserver.observe(toolbarRef.current)\n\t\treturn () => observer.disconnect()\n\t}, [contentSizeUpdateCounter])\n\n\tuseEffect(() => {\n\t\tlet lastContentSizeUpdateCounter = contentSizeUpdateCounter.get()\n\t\treturn react('toolbar position', function updateToolbarPositionAndDisplay() {\n\t\t\tconst toolbarElm = toolbarRef.current\n\t\t\tif (!toolbarElm) return\n\n\t\t\tconst nextContentSizeUpdateCounter = contentSizeUpdateCounter.get()\n\n\t\t\t// capture / force this to update when...\n\t\t\teditor.getCamera() // the camera moves\n\t\t\tcontentSizeUpdateCounter.get() // the toolbar size changes\n\t\t\t// undefined here means that we can't show the toolbar due to an incompatible position\n\t\t\tconst position = getToolbarScreenPosition(editor, toolbarElm, getSelectionBounds)\n\n\t\t\t// todo: when the toolbar is hidden due to the selection being off screen, it should be hidden immediately\n\t\t\t// rather than waiting for the position to settle. This is different than when the position changes due to\n\t\t\t// a change in the user's selection.\n\t\t\tif (!position) {\n\t\t\t\tif (rCouldShowToolbar.current) {\n\t\t\t\t\t// If we don't have a position, then we're not showing the toolbar\n\t\t\t\t\trCouldShowToolbar.current = false\n\t\t\t\t\tsetHasValidToolbarPosition(false)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// If the camera state is moving, we want to immediately update the position\n\t\t\t\t// todo: consider hiding the toolbar while the camera is moving\n\t\t\t\tconst cameraState = editor.getCameraState()\n\t\t\t\tif (cameraState === 'moving') {\n\t\t\t\t\t// ...if we wanted this to avoid prematurely updating any positions, we'd need\n\t\t\t\t\t// to have the last updated position in page space, so that we could convert\n\t\t\t\t\t// it to screen space and update it here\n\t\t\t\t\tconst elm = toolbarRef.current\n\t\t\t\t\telm.style.setProperty('transform', `translate(${position.x}px, ${position.y}px)`)\n\t\t\t\t} else {\n\t\t\t\t\tconst moveImmediately = lastContentSizeUpdateCounter !== nextContentSizeUpdateCounter\n\t\t\t\t\t// Schedule a move to its next location\n\t\t\t\t\tmove(position.x, position.y, moveImmediately)\n\t\t\t\t}\n\n\t\t\t\t// Finally, if the toolbar was previously hidden, show it again\n\t\t\t\tif (!rCouldShowToolbar.current) {\n\t\t\t\t\trCouldShowToolbar.current = true\n\t\t\t\t\tsetHasValidToolbarPosition(true)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlastContentSizeUpdateCounter = nextContentSizeUpdateCounter\n\t\t})\n\t}, [editor, getSelectionBounds, contentSizeUpdateCounter, move])\n\n\tconst cameraState = useValue('camera state', () => editor.getCameraState(), [editor])\n\n\t// Send the hide or show events based on whether the user is clicking\n\t// and whether the toolbar's position is valid\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && HIDE_TOOLBAR_WHEN_CAMERA_IS_MOVING) {\n\t\t\thide(true)\n\t\t\treturn\n\t\t}\n\n\t\tif (isMousingDown || !hasValidToolbarPosition) {\n\t\t\thide()\n\t\t\treturn\n\t\t}\n\n\t\tshow()\n\t}, [hasValidToolbarPosition, cameraState, isMousingDown, show, hide])\n\n\t// When the visibility changes, update the toolbar's visibility\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.dataset.visible = `${isVisible}`\n\t}, [isVisible, position])\n\n\t// When the position changes, update the toolbar's position on screen\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.style.setProperty('transform', `translate(${position.x}px, ${position.y}px)`)\n\t}, [position])\n\n\t// When the interactivity changes, update the toolbar's interactivity\n\tuseLayoutEffect(() => {\n\t\tconst elm = toolbarRef.current\n\t\tif (!elm) return\n\t\telm.dataset.interactive = `${isInteractive}`\n\t}, [isInteractive])\n\n\treturn (\n\t\t<div\n\t\t\tref={toolbarRef}\n\t\t\tdata-interactive={false}\n\t\t\tdata-visible={false}\n\t\t\tdata-testid=\"contextual-toolbar\"\n\t\t\tclassName={classNames('tlui-contextual-toolbar', className)}\n\t\t\tonPointerDown={stopEventPropagation}\n\t\t>\n\t\t\t<TldrawUiToolbar\n\t\t\t\torientation=\"horizontal\"\n\t\t\t\tclassName=\"tlui-menu\"\n\t\t\t\tlabel={label}\n\t\t\t\ttooltipSide=\"top\"\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</TldrawUiToolbar>\n\t\t</div>\n\t)\n}\n\n// For convenience, let's work just with boxes here\n/** @internal */\nexport function rectToBox(rect: DOMRect): Box {\n\treturn new Box(rect.x, rect.y, rect.width, rect.height)\n}\n\nexport function getToolbarScreenPosition(\n\teditor: Editor,\n\ttoolbarElm: HTMLElement,\n\tgetSelectionBounds: () => Box | undefined\n) {\n\tconst selectionBounds = getSelectionBounds()?.clone()\n\tif (!selectionBounds) return\n\n\t// Offset the selection bounds by the viewport screen bounds (if the editor is scrolled or inset, etc)\n\tconst vsb = editor.getViewportScreenBounds()\n\tselectionBounds.x -= vsb.x\n\tselectionBounds.y -= vsb.y\n\n\t// If the selection bounds are too far off of the screen, don't show the toolbar\n\tif (\n\t\tselectionBounds.midY < SCREEN_MARGIN ||\n\t\tselectionBounds.midY > vsb.h - SCREEN_MARGIN ||\n\t\tselectionBounds.midX < SCREEN_MARGIN ||\n\t\tselectionBounds.midX > vsb.w - SCREEN_MARGIN\n\t) {\n\t\treturn\n\t}\n\n\t// Get the toolbar's screen rect as a box. Do this after we verify that there is at least one selection.\n\tconst toolbarBounds = rectToBox(toolbarElm.getBoundingClientRect())\n\n\t// Chance these are NaN? Rare case.\n\tif (!toolbarBounds.width || !toolbarBounds.height) return\n\n\t// Thrashy, only do this if we're showing the toolbar\n\t// ! this might not be needed, the container never scrolls\n\tconst { scrollLeft, scrollTop } = editor.getContainer()\n\n\t// We want to position the toolbar so that it is centered over the selection\n\t// except in the cases where it would extend off the edge of the screen.\n\n\t// Start by placing the top left corner of the toolbar so that the\n\t// toolbar would be centered above the section bounds, bumped up by the\n\n\tlet x = LEFT_ALIGN_TOOLBAR ? selectionBounds.x : selectionBounds.midX - toolbarBounds.w / 2\n\tlet y = selectionBounds.y - toolbarBounds.h - TOOLBAR_GAP\n\n\t// Clamp the position on screen.\n\tx = clamp(x, SCREEN_MARGIN, vsb.w - toolbarBounds.w - SCREEN_MARGIN)\n\ty = clamp(y, SCREEN_MARGIN, vsb.h - toolbarBounds.h - SCREEN_MARGIN)\n\n\t// Offset the position by the container's scroll position\n\tx += scrollLeft\n\ty += scrollTop\n\n\t// Round the position to the nearest pixel\n\tx = Math.round(x)\n\ty = Math.round(y)\n\n\treturn { x, y }\n}\n\nfunction sufficientlyDistant(curr: Vec, next: Vec, changeOnlyWhenYChanges: boolean) {\n\tif (changeOnlyWhenYChanges) {\n\t\treturn Vec.Sub(next, curr).y ** 2 >= MIN_DISTANCE_TO_REPOSITION_SQUARED\n\t}\n\treturn Vec.Len2(Vec.Sub(next, curr)) >= MIN_DISTANCE_TO_REPOSITION_SQUARED\n}\n\nexport function useToolbarVisibilityStateMachine(changeOnlyWhenYChanges: boolean) {\n\tconst editor = useEditor()\n\n\tconst rState = useRef<\n\t\t{ name: 'hidden' } | { name: 'showing' } | { name: 'shown' } | { name: 'hiding' }\n\t>({ name: 'hidden' })\n\n\t// The toolbar should only be interactive when in the 'shown' state\n\tconst [isInteractive, setIsInteractive] = useState(false)\n\n\t// The toolbar is visible in the 'shown' and 'hiding' states\n\tconst [isVisible, setIsVisible] = useState(false)\n\n\t// The position is updated when entering the 'shown' state or when moving while in the 'shown' state\n\tconst [position, setPosition] = useState({ x: -1000, y: -1000 })\n\n\t// The toolbar's current position\n\tconst rCurrPosition = useRef(new Vec(-1000, -1000))\n\n\t// The toolbar's proposed next position\n\tconst rNextPosition = useRef(new Vec(-1000, -1000))\n\n\t// A timeout needs to be completed before the toolbar is shown or hidden\n\tconst rStableVisibilityTimeout = useRef<any>(-1)\n\n\t// A timeout needs to be completed before the toolbar's position changes moved\n\tconst rStablePositionTimeout = useRef<any>(-1)\n\n\t/**\n\t * Send the 'move' event whenever something happens that would cause the toolbar's position to change.\n\t * Any update here will cause\n\t * If the state is 'shown', it will start a new timeout that will update the toolbar's position after it completes.\n\t */\n\tconst move = useCallback(\n\t\t(x: number, y: number, immediate = false) => {\n\t\t\t// Update the next proposed position\n\t\t\trNextPosition.current.x = x\n\t\t\trNextPosition.current.y = y\n\n\t\t\t// If the toolbar is not yet visible, don't do anything\n\t\t\tif (rState.current.name === 'hidden' || rState.current.name === 'showing') return\n\n\t\t\t// If showing or hiding, cancel the position timeout and start a new one.\n\t\t\t// When the timeout ends, if we're in the 'shown' state and the position has changed sufficiently\n\t\t\t// from the last visible position, update the position.\n\t\t\tclearTimeout(rStablePositionTimeout.current)\n\n\t\t\tconst flushMove = () => {\n\t\t\t\tif (\n\t\t\t\t\trState.current.name === 'shown' &&\n\t\t\t\t\tsufficientlyDistant(rNextPosition.current, rCurrPosition.current, changeOnlyWhenYChanges)\n\t\t\t\t) {\n\t\t\t\t\tconst { x, y } = rNextPosition.current\n\t\t\t\t\trCurrPosition.current = new Vec(x, y)\n\t\t\t\t\tif (immediate) {\n\t\t\t\t\t\tflushSync(() => setPosition({ x, y }))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetPosition({ x, y })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (immediate) {\n\t\t\t\tflushMove()\n\t\t\t} else {\n\t\t\t\trStablePositionTimeout.current = editor.timers.setTimeout(flushMove, MOVE_TIMEOUT)\n\t\t\t}\n\t\t},\n\t\t[editor, changeOnlyWhenYChanges]\n\t)\n\n\t/**\n\t * Send the hide event whenever a change occurs that would cause the toolbar to become invisible.\n\t * If the state is 'shown', it will enter 'hiding' and then 'hidden' after a timeout completes.\n\t * If the state is 'showing', it will cancel the visibility timeout and enter 'hidden' immediately.\n\t */\n\tconst hide = useCallback(\n\t\t(immediate = false) => {\n\t\t\tswitch (rState.current.name) {\n\t\t\t\tcase 'showing': {\n\t\t\t\t\tclearTimeout(rStableVisibilityTimeout.current)\n\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'shown': {\n\t\t\t\t\trState.current = { name: 'hiding' }\n\t\t\t\t\tsetIsInteractive(false) // when leaving shown, turn back on interactions\n\n\t\t\t\t\tif (immediate) {\n\t\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\t\tsetIsVisible(false)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trStableVisibilityTimeout.current = editor.timers.setTimeout(() => {\n\t\t\t\t\t\t\trState.current = { name: 'hidden' }\n\t\t\t\t\t\t\tsetIsVisible(false)\n\t\t\t\t\t\t}, HIDE_VISIBILITY_TIMEOUT)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\t// noop\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\t/**\n\t * Send the show event whenever a change occurs that would cause the toolbar to become visible.\n\t * If the state is 'hidden', it will enter 'showing' and then 'shown' after a timeout completes.\n\t * If the state is 'hiding', it will cancel the visibility timeout and enter 'shown' immediately.\n\t */\n\tconst show = useCallback(() => {\n\t\tswitch (rState.current.name) {\n\t\t\tcase 'hidden': {\n\t\t\t\trState.current = { name: 'showing' }\n\t\t\t\trStableVisibilityTimeout.current = editor.timers.setTimeout(() => {\n\t\t\t\t\t// position\n\t\t\t\t\tconst { x, y } = rNextPosition.current\n\t\t\t\t\trCurrPosition.current = new Vec(x, y)\n\t\t\t\t\tsetPosition({ x, y })\n\n\t\t\t\t\trState.current = { name: 'shown' }\n\t\t\t\t\tsetIsVisible(true)\n\t\t\t\t\tsetIsInteractive(true)\n\t\t\t\t}, SHOW_VISIBILITY_TIMEOUT)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'hiding': {\n\t\t\t\t// Go back to shown immediately\n\t\t\t\tclearTimeout(rStableVisibilityTimeout.current)\n\t\t\t\trState.current = { name: 'shown' }\n\t\t\t\tsetIsInteractive(true) // when entering shown, turn back on interactions\n\t\t\t\tmove(rNextPosition.current.x, rNextPosition.current.y)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// noop\n\t\t\t}\n\t\t}\n\t}, [editor, move])\n\n\treturn { isVisible, isInteractive, show, hide, move, position }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["position", "cameraState", "classNames", "x", "y"]
7
7
  }
@@ -32,9 +32,11 @@ __export(TldrawUiSlider_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(TldrawUiSlider_exports);
34
34
  var import_jsx_runtime = require("react/jsx-runtime");
35
+ var import_editor = require("@tldraw/editor");
35
36
  var import_radix_ui = require("radix-ui");
36
37
  var import_react = __toESM(require("react"));
37
38
  var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
39
+ var import_TldrawUiTooltip = require("./TldrawUiTooltip");
38
40
  const TldrawUiSlider = import_react.default.forwardRef(function Slider({
39
41
  onHistoryMark,
40
42
  title,
@@ -47,6 +49,7 @@ const TldrawUiSlider = import_react.default.forwardRef(function Slider({
47
49
  ariaValueModifier = 1
48
50
  }, ref) {
49
51
  const msg = (0, import_useTranslation.useTranslation)();
52
+ const [titleAndLabel, setTitleAndLabel] = (0, import_react.useState)("");
50
53
  const [tabIndex, setTabIndex] = (0, import_react.useState)(-1);
51
54
  (0, import_react.useEffect)(() => {
52
55
  setTabIndex(0);
@@ -58,14 +61,25 @@ const TldrawUiSlider = import_react.default.forwardRef(function Slider({
58
61
  [onValueChange]
59
62
  );
60
63
  const handlePointerDown = (0, import_react.useCallback)(() => {
64
+ import_TldrawUiTooltip.tooltipManager.hideAllTooltips();
61
65
  onHistoryMark("click slider");
62
66
  }, [onHistoryMark]);
67
+ (0, import_react.useEffect)(() => {
68
+ const timeout = import_editor.tltime.setTimeout(
69
+ "set title and label",
70
+ () => {
71
+ setTitleAndLabel(title + " \u2014 " + msg(label));
72
+ },
73
+ 0
74
+ );
75
+ return () => clearTimeout(timeout);
76
+ }, [label, msg, title]);
63
77
  const handleKeyEvent = (0, import_react.useCallback)((event) => {
64
78
  if (event.key === "Tab") {
65
79
  event.stopPropagation();
66
80
  }
67
81
  }, []);
68
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-slider__container", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
82
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-slider__container", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TldrawUiTooltip.TldrawUiTooltip, { content: titleAndLabel, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
69
83
  import_radix_ui.Slider.Root,
70
84
  {
71
85
  "data-testid": testId,
@@ -79,7 +93,6 @@ const TldrawUiSlider = import_react.default.forwardRef(function Slider({
79
93
  onValueChange: handleValueChange,
80
94
  onKeyDownCapture: handleKeyEvent,
81
95
  onKeyUpCapture: handleKeyEvent,
82
- title: title + " \u2014 " + msg(label),
83
96
  children: [
84
97
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Slider.Track, { className: "tlui-slider__track", dir: "ltr", children: value !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Slider.Range, { className: "tlui-slider__range", dir: "ltr" }) }),
85
98
  value !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -88,7 +101,7 @@ const TldrawUiSlider = import_react.default.forwardRef(function Slider({
88
101
  "aria-valuemin": (min ?? 0) * ariaValueModifier,
89
102
  "aria-valuenow": value * ariaValueModifier,
90
103
  "aria-valuemax": steps * ariaValueModifier,
91
- "aria-label": title + " \u2014 " + msg(label),
104
+ "aria-label": titleAndLabel,
92
105
  className: "tlui-slider__thumb",
93
106
  dir: "ltr",
94
107
  ref,
@@ -97,6 +110,6 @@ const TldrawUiSlider = import_react.default.forwardRef(function Slider({
97
110
  )
98
111
  ]
99
112
  }
100
- ) });
113
+ ) }) });
101
114
  });
102
115
  //# sourceMappingURL=TldrawUiSlider.js.map