tldraw 3.16.0-canary.c1bcdabc9513 → 3.16.0-canary.c7d3f7d5729d

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 (123) hide show
  1. package/dist-cjs/index.d.ts +22 -2
  2. package/dist-cjs/index.js +5 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/Tldraw.js +12 -2
  5. package/dist-cjs/lib/Tldraw.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js +5 -4
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +6 -0
  9. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -0
  11. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/ui/TldrawUi.js +13 -12
  13. package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
  14. package/dist-cjs/lib/ui/assetUrls.js +13 -10
  15. package/dist-cjs/lib/ui/assetUrls.js.map +2 -2
  16. package/dist-cjs/lib/ui/components/{FollowingIndicator.js → DefaultFollowingIndicator.js} +6 -6
  17. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +7 -0
  18. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +6 -6
  19. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +1 -1
  20. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js +1 -1
  21. package/dist-cjs/lib/ui/components/StylePanel/DropdownPicker.js.map +2 -2
  22. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  23. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  24. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +38 -9
  25. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  26. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js +15 -3
  27. package/dist-cjs/lib/ui/components/Toolbar/DefaultVideoToolbarContent.js.map +2 -2
  28. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
  29. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  30. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +10 -1
  31. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  32. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +17 -4
  33. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  34. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +35 -37
  35. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  36. package/dist-cjs/lib/ui/context/actions.js +16 -2
  37. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  38. package/dist-cjs/lib/ui/context/components.js +2 -0
  39. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  40. package/dist-cjs/lib/ui/context/events.js.map +1 -1
  41. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  42. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  43. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  44. package/dist-cjs/lib/ui/kbd-utils.js +9 -3
  45. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  46. package/dist-cjs/lib/ui/version.js +3 -3
  47. package/dist-cjs/lib/ui/version.js.map +1 -1
  48. package/dist-esm/index.d.mts +22 -2
  49. package/dist-esm/index.mjs +7 -2
  50. package/dist-esm/index.mjs.map +2 -2
  51. package/dist-esm/lib/Tldraw.mjs +14 -4
  52. package/dist-esm/lib/Tldraw.mjs.map +2 -2
  53. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -4
  54. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  55. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +6 -0
  56. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  57. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -0
  58. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  59. package/dist-esm/lib/ui/TldrawUi.mjs +13 -12
  60. package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
  61. package/dist-esm/lib/ui/assetUrls.mjs +13 -10
  62. package/dist-esm/lib/ui/assetUrls.mjs.map +2 -2
  63. package/dist-esm/lib/ui/components/{FollowingIndicator.mjs → DefaultFollowingIndicator.mjs} +3 -3
  64. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +7 -0
  65. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +6 -6
  66. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +1 -1
  67. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs +1 -1
  68. package/dist-esm/lib/ui/components/StylePanel/DropdownPicker.mjs.map +2 -2
  69. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  70. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  71. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +38 -9
  72. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  73. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs +15 -3
  74. package/dist-esm/lib/ui/components/Toolbar/DefaultVideoToolbarContent.mjs.map +2 -2
  75. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  76. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  77. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +10 -1
  78. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  79. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +17 -4
  80. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  81. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +35 -37
  82. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  83. package/dist-esm/lib/ui/context/actions.mjs +16 -2
  84. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  85. package/dist-esm/lib/ui/context/components.mjs +2 -0
  86. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  87. package/dist-esm/lib/ui/context/events.mjs.map +1 -1
  88. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  89. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  90. package/dist-esm/lib/ui/kbd-utils.mjs +9 -3
  91. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  92. package/dist-esm/lib/ui/version.mjs +3 -3
  93. package/dist-esm/lib/ui/version.mjs.map +1 -1
  94. package/package.json +3 -3
  95. package/src/index.ts +3 -1
  96. package/src/lib/Tldraw.tsx +15 -2
  97. package/src/lib/defaultExternalContentHandlers.ts +12 -4
  98. package/src/lib/shapes/frame/FrameShapeUtil.tsx +8 -0
  99. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -0
  100. package/src/lib/ui/TldrawUi.tsx +16 -10
  101. package/src/lib/ui/assetUrls.ts +13 -10
  102. package/src/lib/ui/components/{FollowingIndicator.tsx → DefaultFollowingIndicator.tsx} +2 -1
  103. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +6 -6
  104. package/src/lib/ui/components/StylePanel/DropdownPicker.tsx +1 -1
  105. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  106. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +32 -15
  107. package/src/lib/ui/components/Toolbar/DefaultVideoToolbarContent.tsx +12 -4
  108. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
  109. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +6 -1
  110. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +50 -30
  111. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +26 -22
  112. package/src/lib/ui/context/actions.tsx +16 -2
  113. package/src/lib/ui/context/components.tsx +3 -0
  114. package/src/lib/ui/context/events.tsx +1 -1
  115. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  116. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  117. package/src/lib/ui/kbd-utils.ts +10 -3
  118. package/src/lib/ui/version.ts +3 -3
  119. package/src/lib/ui.css +3 -0
  120. package/src/test/custom-clipping.test.ts +436 -0
  121. package/tldraw.css +11 -0
  122. package/dist-cjs/lib/ui/components/FollowingIndicator.js.map +0 -7
  123. package/dist-esm/lib/ui/components/FollowingIndicator.mjs.map +0 -7
@@ -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", "x", "y"]
7
7
  }
@@ -1,7 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { tltime } from "@tldraw/editor";
2
3
  import { Slider as _Slider } from "radix-ui";
3
4
  import React, { useCallback, useEffect, useState } from "react";
4
5
  import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
6
+ import { TldrawUiTooltip, tooltipManager } from "./TldrawUiTooltip.mjs";
5
7
  const TldrawUiSlider = React.forwardRef(function Slider({
6
8
  onHistoryMark,
7
9
  title,
@@ -14,6 +16,7 @@ const TldrawUiSlider = React.forwardRef(function Slider({
14
16
  ariaValueModifier = 1
15
17
  }, ref) {
16
18
  const msg = useTranslation();
19
+ const [titleAndLabel, setTitleAndLabel] = useState("");
17
20
  const [tabIndex, setTabIndex] = useState(-1);
18
21
  useEffect(() => {
19
22
  setTabIndex(0);
@@ -25,14 +28,25 @@ const TldrawUiSlider = React.forwardRef(function Slider({
25
28
  [onValueChange]
26
29
  );
27
30
  const handlePointerDown = useCallback(() => {
31
+ tooltipManager.hideAllTooltips();
28
32
  onHistoryMark("click slider");
29
33
  }, [onHistoryMark]);
34
+ useEffect(() => {
35
+ const timeout = tltime.setTimeout(
36
+ "set title and label",
37
+ () => {
38
+ setTitleAndLabel(title + " \u2014 " + msg(label));
39
+ },
40
+ 0
41
+ );
42
+ return () => clearTimeout(timeout);
43
+ }, [label, msg, title]);
30
44
  const handleKeyEvent = useCallback((event) => {
31
45
  if (event.key === "Tab") {
32
46
  event.stopPropagation();
33
47
  }
34
48
  }, []);
35
- return /* @__PURE__ */ jsx("div", { className: "tlui-slider__container", children: /* @__PURE__ */ jsxs(
49
+ return /* @__PURE__ */ jsx("div", { className: "tlui-slider__container", children: /* @__PURE__ */ jsx(TldrawUiTooltip, { content: titleAndLabel, children: /* @__PURE__ */ jsxs(
36
50
  _Slider.Root,
37
51
  {
38
52
  "data-testid": testId,
@@ -46,7 +60,6 @@ const TldrawUiSlider = React.forwardRef(function Slider({
46
60
  onValueChange: handleValueChange,
47
61
  onKeyDownCapture: handleKeyEvent,
48
62
  onKeyUpCapture: handleKeyEvent,
49
- title: title + " \u2014 " + msg(label),
50
63
  children: [
51
64
  /* @__PURE__ */ jsx(_Slider.Track, { className: "tlui-slider__track", dir: "ltr", children: value !== null && /* @__PURE__ */ jsx(_Slider.Range, { className: "tlui-slider__range", dir: "ltr" }) }),
52
65
  value !== null && /* @__PURE__ */ jsx(
@@ -55,7 +68,7 @@ const TldrawUiSlider = React.forwardRef(function Slider({
55
68
  "aria-valuemin": (min ?? 0) * ariaValueModifier,
56
69
  "aria-valuenow": value * ariaValueModifier,
57
70
  "aria-valuemax": steps * ariaValueModifier,
58
- "aria-label": title + " \u2014 " + msg(label),
71
+ "aria-label": titleAndLabel,
59
72
  className: "tlui-slider__thumb",
60
73
  dir: "ltr",
61
74
  ref,
@@ -64,7 +77,7 @@ const TldrawUiSlider = React.forwardRef(function Slider({
64
77
  )
65
78
  ]
66
79
  }
67
- ) });
80
+ ) }) });
68
81
  });
69
82
  export {
70
83
  TldrawUiSlider
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiSlider.tsx"],
4
- "sourcesContent": ["import { Slider as _Slider } from 'radix-ui'\nimport React, { useCallback, useEffect, useState } from 'react'\nimport { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiSliderProps {\n\tmin?: number\n\tsteps: number\n\tvalue: number | null\n\tlabel: string\n\ttitle: string\n\tonValueChange(value: number): void\n\tonHistoryMark(id: string): void\n\t'data-testid'?: string\n\tariaValueModifier?: number\n}\n\n/** @public @react */\nexport const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(function Slider(\n\t{\n\t\tonHistoryMark,\n\t\ttitle,\n\t\tmin,\n\t\tsteps,\n\t\tvalue,\n\t\tlabel,\n\t\tonValueChange,\n\t\t['data-testid']: testId,\n\t\tariaValueModifier = 1,\n\t}: TLUiSliderProps,\n\tref\n) {\n\tconst msg = useTranslation()\n\n\t// XXX: Radix starts out our slider with a tabIndex of 0\n\t// This causes some tab focusing issues, most prevelant in MobileStylePanel,\n\t// where it grabs the focus. This works around it.\n\tconst [tabIndex, setTabIndex] = useState(-1)\n\tuseEffect(() => {\n\t\tsetTabIndex(0)\n\t}, [])\n\n\tconst handleValueChange = useCallback(\n\t\t(value: number[]) => {\n\t\t\tonValueChange(value[0])\n\t\t},\n\t\t[onValueChange]\n\t)\n\n\tconst handlePointerDown = useCallback(() => {\n\t\tonHistoryMark('click slider')\n\t}, [onHistoryMark])\n\n\t// N.B. Annoying. For a11y purposes, we need Tab to work.\n\t// For some reason, Radix has some custom behavior here\n\t// that interferes with tabbing past the slider and then\n\t// you get stuck in the slider.\n\tconst handleKeyEvent = useCallback((event: React.KeyboardEvent) => {\n\t\tif (event.key === 'Tab') {\n\t\t\tevent.stopPropagation()\n\t\t}\n\t}, [])\n\n\treturn (\n\t\t<div className=\"tlui-slider__container\">\n\t\t\t<_Slider.Root\n\t\t\t\tdata-testid={testId}\n\t\t\t\tclassName=\"tlui-slider\"\n\t\t\t\tdir=\"ltr\"\n\t\t\t\tmin={min ?? 0}\n\t\t\t\tmax={steps}\n\t\t\t\tstep={1}\n\t\t\t\tvalue={value !== null ? [value] : undefined}\n\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonKeyDownCapture={handleKeyEvent}\n\t\t\t\tonKeyUpCapture={handleKeyEvent}\n\t\t\t\ttitle={title + ' \u2014 ' + msg(label as TLUiTranslationKey)}\n\t\t\t>\n\t\t\t\t<_Slider.Track className=\"tlui-slider__track\" dir=\"ltr\">\n\t\t\t\t\t{value !== null && <_Slider.Range className=\"tlui-slider__range\" dir=\"ltr\" />}\n\t\t\t\t</_Slider.Track>\n\t\t\t\t{value !== null && (\n\t\t\t\t\t<_Slider.Thumb\n\t\t\t\t\t\taria-valuemin={(min ?? 0) * ariaValueModifier}\n\t\t\t\t\t\taria-valuenow={value * ariaValueModifier}\n\t\t\t\t\t\taria-valuemax={steps * ariaValueModifier}\n\t\t\t\t\t\taria-label={title + ' \u2014 ' + msg(label as TLUiTranslationKey)}\n\t\t\t\t\t\tclassName=\"tlui-slider__thumb\"\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\ttabIndex={tabIndex}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</_Slider.Root>\n\t\t</div>\n\t)\n})\n"],
5
- "mappings": "AAkEG,SAeqB,KAfrB;AAlEH,SAAS,UAAU,eAAe;AAClC,OAAO,SAAS,aAAa,WAAW,gBAAgB;AAExD,SAAS,sBAAsB;AAgBxB,MAAM,iBAAiB,MAAM,WAA4C,SAAS,OACxF;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,CAAC,aAAa,GAAG;AAAA,EACjB,oBAAoB;AACrB,GACA,KACC;AACD,QAAM,MAAM,eAAe;AAK3B,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,YAAU,MAAM;AACf,gBAAY,CAAC;AAAA,EACd,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB;AAAA,IACzB,CAACA,WAAoB;AACpB,oBAAcA,OAAM,CAAC,CAAC;AAAA,IACvB;AAAA,IACA,CAAC,aAAa;AAAA,EACf;AAEA,QAAM,oBAAoB,YAAY,MAAM;AAC3C,kBAAc,cAAc;AAAA,EAC7B,GAAG,CAAC,aAAa,CAAC;AAMlB,QAAM,iBAAiB,YAAY,CAAC,UAA+B;AAClE,QAAI,MAAM,QAAQ,OAAO;AACxB,YAAM,gBAAgB;AAAA,IACvB;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,SACC,oBAAC,SAAI,WAAU,0BACd;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACA,eAAa;AAAA,MACb,WAAU;AAAA,MACV,KAAI;AAAA,MACJ,KAAK,OAAO;AAAA,MACZ,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO,UAAU,OAAO,CAAC,KAAK,IAAI;AAAA,MAClC,eAAe;AAAA,MACf,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,OAAO,QAAQ,aAAQ,IAAI,KAA2B;AAAA,MAEtD;AAAA,4BAAC,QAAQ,OAAR,EAAc,WAAU,sBAAqB,KAAI,OAChD,oBAAU,QAAQ,oBAAC,QAAQ,OAAR,EAAc,WAAU,sBAAqB,KAAI,OAAM,GAC5E;AAAA,QACC,UAAU,QACV;AAAA,UAAC,QAAQ;AAAA,UAAR;AAAA,YACA,kBAAgB,OAAO,KAAK;AAAA,YAC5B,iBAAe,QAAQ;AAAA,YACvB,iBAAe,QAAQ;AAAA,YACvB,cAAY,QAAQ,aAAQ,IAAI,KAA2B;AAAA,YAC3D,WAAU;AAAA,YACV,KAAI;AAAA,YACJ;AAAA,YACA;AAAA;AAAA,QACD;AAAA;AAAA;AAAA,EAEF,GACD;AAEF,CAAC;",
4
+ "sourcesContent": ["import { tltime } from '@tldraw/editor'\nimport { Slider as _Slider } from 'radix-ui'\nimport React, { useCallback, useEffect, useState } from 'react'\nimport { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiTooltip, tooltipManager } from './TldrawUiTooltip'\n\n/** @public */\nexport interface TLUiSliderProps {\n\tmin?: number\n\tsteps: number\n\tvalue: number | null\n\tlabel: string\n\ttitle: string\n\tonValueChange(value: number): void\n\tonHistoryMark(id: string): void\n\t'data-testid'?: string\n\tariaValueModifier?: number\n}\n\n/** @public @react */\nexport const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(function Slider(\n\t{\n\t\tonHistoryMark,\n\t\ttitle,\n\t\tmin,\n\t\tsteps,\n\t\tvalue,\n\t\tlabel,\n\t\tonValueChange,\n\t\t['data-testid']: testId,\n\t\tariaValueModifier = 1,\n\t}: TLUiSliderProps,\n\tref\n) {\n\tconst msg = useTranslation()\n\tconst [titleAndLabel, setTitleAndLabel] = useState('')\n\n\t// XXX: Radix starts out our slider with a tabIndex of 0\n\t// This causes some tab focusing issues, most prevelant in MobileStylePanel,\n\t// where it grabs the focus. This works around it.\n\tconst [tabIndex, setTabIndex] = useState(-1)\n\tuseEffect(() => {\n\t\tsetTabIndex(0)\n\t}, [])\n\n\tconst handleValueChange = useCallback(\n\t\t(value: number[]) => {\n\t\t\tonValueChange(value[0])\n\t\t},\n\t\t[onValueChange]\n\t)\n\n\tconst handlePointerDown = useCallback(() => {\n\t\ttooltipManager.hideAllTooltips()\n\t\tonHistoryMark('click slider')\n\t}, [onHistoryMark])\n\n\t// N.B. This is a bit silly. The Radix slider auto-focuses which\n\t// triggers TldrawUiTooltip handleFocus when we dbl-click to edit an image,\n\t// which in turn makes the tooltip display prematurely.\n\t// This makes it wait until we've focused to show the tooltip.\n\tuseEffect(() => {\n\t\tconst timeout = tltime.setTimeout(\n\t\t\t'set title and label',\n\t\t\t() => {\n\t\t\t\tsetTitleAndLabel(title + ' \u2014 ' + msg(label as TLUiTranslationKey))\n\t\t\t},\n\t\t\t0\n\t\t)\n\t\treturn () => clearTimeout(timeout)\n\t}, [label, msg, title])\n\n\t// N.B. Annoying. For a11y purposes, we need Tab to work.\n\t// For some reason, Radix has some custom behavior here\n\t// that interferes with tabbing past the slider and then\n\t// you get stuck in the slider.\n\tconst handleKeyEvent = useCallback((event: React.KeyboardEvent) => {\n\t\tif (event.key === 'Tab') {\n\t\t\tevent.stopPropagation()\n\t\t}\n\t}, [])\n\n\treturn (\n\t\t<div className=\"tlui-slider__container\">\n\t\t\t<TldrawUiTooltip content={titleAndLabel}>\n\t\t\t\t<_Slider.Root\n\t\t\t\t\tdata-testid={testId}\n\t\t\t\t\tclassName=\"tlui-slider\"\n\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\tmin={min ?? 0}\n\t\t\t\t\tmax={steps}\n\t\t\t\t\tstep={1}\n\t\t\t\t\tvalue={value !== null ? [value] : undefined}\n\t\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\t\tonKeyDownCapture={handleKeyEvent}\n\t\t\t\t\tonKeyUpCapture={handleKeyEvent}\n\t\t\t\t>\n\t\t\t\t\t<_Slider.Track className=\"tlui-slider__track\" dir=\"ltr\">\n\t\t\t\t\t\t{value !== null && <_Slider.Range className=\"tlui-slider__range\" dir=\"ltr\" />}\n\t\t\t\t\t</_Slider.Track>\n\t\t\t\t\t{value !== null && (\n\t\t\t\t\t\t<_Slider.Thumb\n\t\t\t\t\t\t\taria-valuemin={(min ?? 0) * ariaValueModifier}\n\t\t\t\t\t\t\taria-valuenow={value * ariaValueModifier}\n\t\t\t\t\t\t\taria-valuemax={steps * ariaValueModifier}\n\t\t\t\t\t\t\taria-label={titleAndLabel}\n\t\t\t\t\t\t\tclassName=\"tlui-slider__thumb\"\n\t\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\t\ttabIndex={tabIndex}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</_Slider.Root>\n\t\t\t</TldrawUiTooltip>\n\t\t</div>\n\t)\n})\n"],
5
+ "mappings": "AAsFI,SAcqB,KAdrB;AAtFJ,SAAS,cAAc;AACvB,SAAS,UAAU,eAAe;AAClC,OAAO,SAAS,aAAa,WAAW,gBAAgB;AAExD,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB,sBAAsB;AAgBzC,MAAM,iBAAiB,MAAM,WAA4C,SAAS,OACxF;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,CAAC,aAAa,GAAG;AAAA,EACjB,oBAAoB;AACrB,GACA,KACC;AACD,QAAM,MAAM,eAAe;AAC3B,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,EAAE;AAKrD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,YAAU,MAAM;AACf,gBAAY,CAAC;AAAA,EACd,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB;AAAA,IACzB,CAACA,WAAoB;AACpB,oBAAcA,OAAM,CAAC,CAAC;AAAA,IACvB;AAAA,IACA,CAAC,aAAa;AAAA,EACf;AAEA,QAAM,oBAAoB,YAAY,MAAM;AAC3C,mBAAe,gBAAgB;AAC/B,kBAAc,cAAc;AAAA,EAC7B,GAAG,CAAC,aAAa,CAAC;AAMlB,YAAU,MAAM;AACf,UAAM,UAAU,OAAO;AAAA,MACtB;AAAA,MACA,MAAM;AACL,yBAAiB,QAAQ,aAAQ,IAAI,KAA2B,CAAC;AAAA,MAClE;AAAA,MACA;AAAA,IACD;AACA,WAAO,MAAM,aAAa,OAAO;AAAA,EAClC,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC;AAMtB,QAAM,iBAAiB,YAAY,CAAC,UAA+B;AAClE,QAAI,MAAM,QAAQ,OAAO;AACxB,YAAM,gBAAgB;AAAA,IACvB;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,SACC,oBAAC,SAAI,WAAU,0BACd,8BAAC,mBAAgB,SAAS,eACzB;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACA,eAAa;AAAA,MACb,WAAU;AAAA,MACV,KAAI;AAAA,MACJ,KAAK,OAAO;AAAA,MACZ,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO,UAAU,OAAO,CAAC,KAAK,IAAI;AAAA,MAClC,eAAe;AAAA,MACf,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAEhB;AAAA,4BAAC,QAAQ,OAAR,EAAc,WAAU,sBAAqB,KAAI,OAChD,oBAAU,QAAQ,oBAAC,QAAQ,OAAR,EAAc,WAAU,sBAAqB,KAAI,OAAM,GAC5E;AAAA,QACC,UAAU,QACV;AAAA,UAAC,QAAQ;AAAA,UAAR;AAAA,YACA,kBAAgB,OAAO,KAAK;AAAA,YAC5B,iBAAe,QAAQ;AAAA,YACvB,iBAAe,QAAQ;AAAA,YACvB,cAAY;AAAA,YACZ,WAAU;AAAA,YACV,KAAI;AAAA,YACJ;AAAA,YACA;AAAA;AAAA,QACD;AAAA;AAAA;AAAA,EAEF,GACD,GACD;AAEF,CAAC;",
6
6
  "names": ["value"]
7
7
  }
@@ -81,21 +81,16 @@ function TldrawUiTooltipProvider({ children }) {
81
81
  ] }) });
82
82
  }
83
83
  function TooltipSingleton() {
84
- const editor = useMaybeEditor();
85
84
  const [isOpen, setIsOpen] = useState(false);
86
85
  const triggerRef = useRef(null);
87
86
  const isFirstShowRef = useRef(true);
88
- const showTimeoutRef = useRef(null);
89
87
  const currentTooltip = useValue(
90
88
  "current tooltip",
91
89
  () => tooltipManager.getCurrentTooltipData(),
92
90
  []
93
91
  );
94
92
  useEffect(() => {
95
- if (showTimeoutRef.current) {
96
- clearTimeout(showTimeoutRef.current);
97
- showTimeoutRef.current = null;
98
- }
93
+ let timer = null;
99
94
  if (currentTooltip && triggerRef.current) {
100
95
  const activeRect = currentTooltip.targetElement.getBoundingClientRect();
101
96
  const trigger = triggerRef.current;
@@ -106,11 +101,11 @@ function TooltipSingleton() {
106
101
  trigger.style.height = `${activeRect.height}px`;
107
102
  trigger.style.pointerEvents = "none";
108
103
  trigger.style.zIndex = "9999";
109
- if (isFirstShowRef.current && editor) {
110
- showTimeoutRef.current = editor.timers.setTimeout(() => {
104
+ if (isFirstShowRef.current) {
105
+ timer = setTimeout(() => {
111
106
  setIsOpen(true);
112
107
  isFirstShowRef.current = false;
113
- }, currentTooltip.delayDuration ?? editor.options.tooltipDelayMs);
108
+ }, currentTooltip.delayDuration);
114
109
  } else {
115
110
  setIsOpen(true);
116
111
  }
@@ -118,7 +113,12 @@ function TooltipSingleton() {
118
113
  setIsOpen(false);
119
114
  isFirstShowRef.current = true;
120
115
  }
121
- }, [editor, currentTooltip]);
116
+ return () => {
117
+ if (timer !== null) {
118
+ clearTimeout(timer);
119
+ }
120
+ };
121
+ }, [currentTooltip]);
122
122
  if (!currentTooltip) {
123
123
  return null;
124
124
  }
@@ -156,6 +156,7 @@ const TldrawUiTooltip = forwardRef(
156
156
  const hasProvider = useContext(TooltipSingletonContext);
157
157
  const orientationCtx = useTldrawUiOrientation();
158
158
  const sideToUse = side ?? orientationCtx.tooltipSide;
159
+ const camera = useValue("camera", () => editor?.getCamera(), []);
159
160
  useEffect(() => {
160
161
  const currentTooltipId = tooltipId.current;
161
162
  return () => {
@@ -164,35 +165,32 @@ const TldrawUiTooltip = forwardRef(
164
165
  }
165
166
  };
166
167
  }, [editor, hasProvider]);
168
+ useEffect(() => {
169
+ tooltipManager.hideTooltip(editor, tooltipId.current, true);
170
+ }, [editor, camera]);
167
171
  if (disabled || !content) {
168
172
  return /* @__PURE__ */ jsx(Fragment, { children });
169
173
  }
174
+ const delayDurationToUse = delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS);
170
175
  if (!hasProvider) {
171
- return /* @__PURE__ */ jsxs(
172
- _Tooltip.Root,
173
- {
174
- delayDuration: delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS),
175
- disableHoverableContent: true,
176
- children: [
177
- /* @__PURE__ */ jsx(_Tooltip.Trigger, { asChild: true, ref, children }),
178
- /* @__PURE__ */ jsxs(
179
- _Tooltip.Content,
180
- {
181
- className: "tlui-tooltip",
182
- side: sideToUse,
183
- sideOffset,
184
- avoidCollisions: true,
185
- collisionPadding: 8,
186
- dir: "ltr",
187
- children: [
188
- content,
189
- /* @__PURE__ */ jsx(_Tooltip.Arrow, { className: "tlui-tooltip__arrow" })
190
- ]
191
- }
192
- )
193
- ]
194
- }
195
- );
176
+ return /* @__PURE__ */ jsxs(_Tooltip.Root, { delayDuration: delayDurationToUse, disableHoverableContent: true, children: [
177
+ /* @__PURE__ */ jsx(_Tooltip.Trigger, { asChild: true, ref, children }),
178
+ /* @__PURE__ */ jsxs(
179
+ _Tooltip.Content,
180
+ {
181
+ className: "tlui-tooltip",
182
+ side: sideToUse,
183
+ sideOffset,
184
+ avoidCollisions: true,
185
+ collisionPadding: 8,
186
+ dir: "ltr",
187
+ children: [
188
+ content,
189
+ /* @__PURE__ */ jsx(_Tooltip.Arrow, { className: "tlui-tooltip__arrow" })
190
+ ]
191
+ }
192
+ )
193
+ ] });
196
194
  }
197
195
  const child = React.Children.only(children);
198
196
  assert(React.isValidElement(child), "TldrawUiTooltip children must be a single element");
@@ -205,7 +203,7 @@ const TldrawUiTooltip = forwardRef(
205
203
  sideToUse,
206
204
  sideOffset,
207
205
  showOnMobile,
208
- delayDuration
206
+ delayDurationToUse
209
207
  );
210
208
  };
211
209
  const handleMouseLeave = (event) => {
@@ -221,7 +219,7 @@ const TldrawUiTooltip = forwardRef(
221
219
  sideToUse,
222
220
  sideOffset,
223
221
  showOnMobile,
224
- delayDuration
222
+ delayDurationToUse
225
223
  );
226
224
  };
227
225
  const handleBlur = (event) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
4
- "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number | undefined\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number | undefined\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst editor = useMaybeEditor()\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst showTimeoutRef = useRef<number | null>(null)\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\t// Clear any existing show timeout\n\t\tif (showTimeoutRef.current) {\n\t\t\tclearTimeout(showTimeoutRef.current)\n\t\t\tshowTimeoutRef.current = null\n\t\t}\n\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current && editor) {\n\t\t\t\tshowTimeoutRef.current = editor.timers.setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration ?? editor.options.tooltipDelayMs)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\t}, [editor, currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root\n\t\t\t\t\tdelayDuration={\n\t\t\t\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\t\t\t\t\t}\n\t\t\t\t\tdisableHoverableContent\n\t\t\t\t>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDuration\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDuration\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tconst camera = useValue('camera', () => editor?.getCamera(), [])\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseEffect(() => {\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current, true)\n\t\t}, [editor, camera])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tconst delayDurationToUse =\n\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -24,6 +24,7 @@ import { flattenShapesToImages } from "../hooks/useFlatten.mjs";
24
24
  import { useTranslation } from "../hooks/useTranslation/useTranslation.mjs";
25
25
  import { useDefaultHelpers } from "../overrides.mjs";
26
26
  import { useA11y } from "./a11y.mjs";
27
+ import { useTldrawUiComponents } from "./components.mjs";
27
28
  import { useUiEvents } from "./events.mjs";
28
29
  const ActionsContext = React.createContext(null);
29
30
  function supportsDownloadingOriginal(shape, editor) {
@@ -43,6 +44,7 @@ function ActionsProvider({ overrides, children }) {
43
44
  const _editor = useMaybeEditor();
44
45
  const showCollaborationUi = useShowCollaborationUi();
45
46
  const helpers = useDefaultHelpers();
47
+ const components = useTldrawUiComponents();
46
48
  const trackEvent = useUiEvents();
47
49
  const a11y = useA11y();
48
50
  const msg = useTranslation();
@@ -104,7 +106,9 @@ function ActionsProvider({ overrides, children }) {
104
106
  kbd: "cmd+alt+/,ctrl+alt+/",
105
107
  onSelect(source) {
106
108
  trackEvent("open-kbd-shortcuts", { source });
107
- helpers.addDialog({ component: DefaultKeyboardShortcutsDialog });
109
+ helpers.addDialog({
110
+ component: components.KeyboardShortcutsDialog ?? DefaultKeyboardShortcutsDialog
111
+ });
108
112
  }
109
113
  },
110
114
  {
@@ -1566,7 +1570,17 @@ function ActionsProvider({ overrides, children }) {
1566
1570
  return overrides(editor, actions2, helpers);
1567
1571
  }
1568
1572
  return actions2;
1569
- }, [helpers, _editor, trackEvent, overrides, defaultDocumentName, showCollaborationUi, msg, a11y]);
1573
+ }, [
1574
+ helpers,
1575
+ _editor,
1576
+ trackEvent,
1577
+ overrides,
1578
+ defaultDocumentName,
1579
+ showCollaborationUi,
1580
+ msg,
1581
+ a11y,
1582
+ components
1583
+ ]);
1570
1584
  return /* @__PURE__ */ jsx(ActionsContext.Provider, { value: asActions(actions), children });
1571
1585
  }
1572
1586
  function useActions() {