tldraw 3.16.0-canary.6f3aedaa1c01 → 3.16.0-canary.7cd3b8a5bfd6

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 +45 -4
  2. package/dist-cjs/index.js +4 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawScribble.js +1 -1
  5. package/dist-cjs/lib/canvas/TldrawScribble.js.map +2 -2
  6. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js +3 -3
  7. package/dist-cjs/lib/shapes/arrow/elbow/ElbowArrowDebug.js.map +1 -1
  8. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +1 -1
  9. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +1 -1
  10. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +4 -4
  11. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js +1 -1
  13. package/dist-cjs/lib/shapes/frame/components/FrameHeading.js.map +2 -2
  14. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -3
  15. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +1 -1
  16. package/dist-cjs/lib/shapes/shared/ShapeFill.js +1 -1
  17. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  18. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +3 -3
  19. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +1 -1
  20. package/dist-cjs/lib/ui/TldrawUi.js +14 -0
  21. package/dist-cjs/lib/ui/TldrawUi.js.map +3 -3
  22. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js +10 -2
  23. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenu.js.map +2 -2
  24. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +4 -4
  25. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  26. package/dist-cjs/lib/ui/components/MobileStylePanel.js +4 -2
  27. package/dist-cjs/lib/ui/components/MobileStylePanel.js.map +2 -2
  28. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js +1 -1
  29. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbarContent.js.map +2 -2
  30. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js +66 -22
  31. package/dist-cjs/lib/ui/components/Toolbar/DefaultToolbar.js.map +3 -3
  32. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +188 -78
  33. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +3 -3
  34. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +15 -3
  35. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  36. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +144 -160
  37. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  38. package/dist-cjs/lib/ui/components/primitives/layout.js +30 -5
  39. package/dist-cjs/lib/ui/components/primitives/layout.js.map +2 -2
  40. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuContext.js.map +2 -2
  41. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js +25 -12
  42. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuGroup.js.map +2 -2
  43. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +1 -18
  44. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  45. package/dist-cjs/lib/ui/hooks/useTools.js +21 -3
  46. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  47. package/dist-cjs/lib/ui/version.js +3 -3
  48. package/dist-cjs/lib/ui/version.js.map +1 -1
  49. package/dist-esm/index.d.mts +45 -4
  50. package/dist-esm/index.mjs +8 -2
  51. package/dist-esm/index.mjs.map +2 -2
  52. package/dist-esm/lib/canvas/TldrawScribble.mjs +1 -1
  53. package/dist-esm/lib/canvas/TldrawScribble.mjs.map +2 -2
  54. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs +3 -3
  55. package/dist-esm/lib/shapes/arrow/elbow/ElbowArrowDebug.mjs.map +1 -1
  56. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +1 -1
  57. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +1 -1
  58. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +4 -4
  59. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  60. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs +1 -1
  61. package/dist-esm/lib/shapes/frame/components/FrameHeading.mjs.map +2 -2
  62. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -3
  63. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +1 -1
  64. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +1 -1
  65. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  66. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +3 -3
  67. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +1 -1
  68. package/dist-esm/lib/ui/TldrawUi.mjs +16 -2
  69. package/dist-esm/lib/ui/TldrawUi.mjs.map +3 -3
  70. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs +10 -2
  71. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenu.mjs.map +2 -2
  72. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +4 -4
  73. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  74. package/dist-esm/lib/ui/components/MobileStylePanel.mjs +4 -2
  75. package/dist-esm/lib/ui/components/MobileStylePanel.mjs.map +2 -2
  76. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs +1 -1
  77. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbarContent.mjs.map +2 -2
  78. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs +56 -22
  79. package/dist-esm/lib/ui/components/Toolbar/DefaultToolbar.mjs.map +2 -2
  80. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +192 -80
  81. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +3 -3
  82. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +16 -4
  83. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  84. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +153 -162
  85. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  86. package/dist-esm/lib/ui/components/primitives/layout.mjs +31 -6
  87. package/dist-esm/lib/ui/components/primitives/layout.mjs.map +2 -2
  88. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuContext.mjs.map +2 -2
  89. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs +25 -12
  90. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuGroup.mjs.map +2 -2
  91. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +1 -18
  92. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  93. package/dist-esm/lib/ui/hooks/useTools.mjs +22 -3
  94. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  95. package/dist-esm/lib/ui/version.mjs +3 -3
  96. package/dist-esm/lib/ui/version.mjs.map +1 -1
  97. package/package.json +3 -3
  98. package/src/index.ts +5 -0
  99. package/src/lib/canvas/TldrawScribble.tsx +1 -1
  100. package/src/lib/shapes/arrow/elbow/ElbowArrowDebug.tsx +3 -3
  101. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  102. package/src/lib/shapes/frame/FrameShapeUtil.tsx +12 -4
  103. package/src/lib/shapes/frame/components/FrameHeading.tsx +1 -1
  104. package/src/lib/shapes/image/ImageShapeUtil.tsx +3 -3
  105. package/src/lib/shapes/shared/ShapeFill.tsx +1 -1
  106. package/src/lib/shapes/video/VideoShapeUtil.tsx +3 -3
  107. package/src/lib/ui/TldrawUi.tsx +17 -2
  108. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx +13 -2
  109. package/src/lib/ui/components/Minimap/MinimapManager.ts +4 -4
  110. package/src/lib/ui/components/MobileStylePanel.tsx +4 -3
  111. package/src/lib/ui/components/Toolbar/DefaultImageToolbarContent.tsx +1 -1
  112. package/src/lib/ui/components/Toolbar/DefaultToolbar.tsx +55 -24
  113. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +208 -56
  114. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +22 -5
  115. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +175 -180
  116. package/src/lib/ui/components/primitives/layout.tsx +79 -5
  117. package/src/lib/ui/components/primitives/menus/TldrawUiMenuContext.tsx +0 -1
  118. package/src/lib/ui/components/primitives/menus/TldrawUiMenuGroup.tsx +29 -16
  119. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +2 -16
  120. package/src/lib/ui/hooks/useTools.tsx +25 -3
  121. package/src/lib/ui/version.ts +3 -3
  122. package/src/lib/ui.css +346 -243
  123. package/tldraw.css +639 -533
@@ -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(--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 { 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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",
6
6
  "names": ["DefaultImageToolbarContent", "zoom"]
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEditor, usePassThroughWheelEvents, useValue } from "@tldraw/editor";
3
+ import classNames from "classnames";
3
4
  import { memo, useRef } from "react";
4
5
  import { PORTRAIT_BREAKPOINT } from "../../constants.mjs";
5
6
  import { useBreakpoint } from "../../context/breakpoints.mjs";
@@ -7,11 +8,19 @@ import { useTldrawUiComponents } from "../../context/components.mjs";
7
8
  import { useReadonly } from "../../hooks/useReadonly.mjs";
8
9
  import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
9
10
  import { MobileStylePanel } from "../MobileStylePanel.mjs";
11
+ import { TldrawUiOrientationProvider } from "../primitives/layout.mjs";
10
12
  import { TldrawUiToolbar } from "../primitives/TldrawUiToolbar.mjs";
11
13
  import { DefaultToolbarContent } from "./DefaultToolbarContent.mjs";
12
14
  import { OverflowingToolbar } from "./OverflowingToolbar.mjs";
13
15
  import { ToggleToolLockedButton } from "./ToggleToolLockedButton.mjs";
14
- const DefaultToolbar = memo(function DefaultToolbar2({ children }) {
16
+ const DefaultToolbar = memo(function DefaultToolbar2({
17
+ children,
18
+ orientation = "horizontal",
19
+ minItems = 4,
20
+ minSizePx = 310,
21
+ maxItems = 8,
22
+ maxSizePx = 470
23
+ }) {
15
24
  const editor = useEditor();
16
25
  const msg = useTranslation();
17
26
  const breakpoint = useBreakpoint();
@@ -21,27 +30,52 @@ const DefaultToolbar = memo(function DefaultToolbar2({ children }) {
21
30
  usePassThroughWheelEvents(ref);
22
31
  const { ActionsMenu, QuickActions } = useTldrawUiComponents();
23
32
  const showQuickActions = editor.options.actionShortcutsLocation === "menu" ? false : editor.options.actionShortcutsLocation === "toolbar" ? true : breakpoint < PORTRAIT_BREAKPOINT.TABLET;
24
- return /* @__PURE__ */ jsx("div", { ref, className: "tlui-main-toolbar", children: /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__inner", children: [
25
- /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__left", children: [
26
- !isReadonlyMode && /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__extras", children: [
27
- showQuickActions && /* @__PURE__ */ jsxs(
28
- TldrawUiToolbar,
29
- {
30
- orientation: "horizontal",
31
- className: "tlui-main-toolbar__extras__controls",
32
- label: msg("actions-menu.title"),
33
- children: [
34
- QuickActions && /* @__PURE__ */ jsx(QuickActions, {}),
35
- ActionsMenu && /* @__PURE__ */ jsx(ActionsMenu, {})
36
- ]
37
- }
38
- ),
39
- /* @__PURE__ */ jsx(ToggleToolLockedButton, { activeToolId })
40
- ] }),
41
- /* @__PURE__ */ jsx(OverflowingToolbar, { children: children ?? /* @__PURE__ */ jsx(DefaultToolbarContent, {}) })
42
- ] }),
43
- breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && /* @__PURE__ */ jsx("div", { className: "tlui-main-toolbar__tools", children: /* @__PURE__ */ jsx(MobileStylePanel, {}) })
44
- ] }) });
33
+ return /* @__PURE__ */ jsx(
34
+ TldrawUiOrientationProvider,
35
+ {
36
+ orientation,
37
+ tooltipSide: orientation === "horizontal" ? "top" : "right",
38
+ children: /* @__PURE__ */ jsx(
39
+ "div",
40
+ {
41
+ ref,
42
+ className: classNames("tlui-main-toolbar", `tlui-main-toolbar--${orientation}`),
43
+ children: /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__inner", children: [
44
+ /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__left", children: [
45
+ !isReadonlyMode && /* @__PURE__ */ jsxs("div", { className: "tlui-main-toolbar__extras", children: [
46
+ showQuickActions && /* @__PURE__ */ jsxs(
47
+ TldrawUiToolbar,
48
+ {
49
+ orientation,
50
+ className: "tlui-main-toolbar__extras__controls",
51
+ label: msg("actions-menu.title"),
52
+ children: [
53
+ QuickActions && /* @__PURE__ */ jsx(QuickActions, {}),
54
+ ActionsMenu && /* @__PURE__ */ jsx(ActionsMenu, {})
55
+ ]
56
+ }
57
+ ),
58
+ /* @__PURE__ */ jsx(ToggleToolLockedButton, { activeToolId })
59
+ ] }),
60
+ /* @__PURE__ */ jsx(
61
+ OverflowingToolbar,
62
+ {
63
+ orientation,
64
+ sizingParentClassName: "tlui-main-toolbar",
65
+ minItems,
66
+ maxItems,
67
+ minSizePx,
68
+ maxSizePx,
69
+ children: children ?? /* @__PURE__ */ jsx(DefaultToolbarContent, {})
70
+ }
71
+ )
72
+ ] }),
73
+ breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && /* @__PURE__ */ jsx("div", { className: "tlui-main-toolbar__tools tlui-main-toolbar__mobile-style-panel", children: /* @__PURE__ */ jsx(MobileStylePanel, {}) })
74
+ ] })
75
+ }
76
+ )
77
+ }
78
+ );
45
79
  });
46
80
  export {
47
81
  DefaultToolbar
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultToolbar.tsx"],
4
- "sourcesContent": ["import { useEditor, usePassThroughWheelEvents, useValue } from '@tldraw/editor'\nimport { ReactNode, memo, useRef } from 'react'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { useTldrawUiComponents } from '../../context/components'\nimport { useReadonly } from '../../hooks/useReadonly'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { MobileStylePanel } from '../MobileStylePanel'\nimport { TldrawUiToolbar } from '../primitives/TldrawUiToolbar'\nimport { DefaultToolbarContent } from './DefaultToolbarContent'\nimport { OverflowingToolbar } from './OverflowingToolbar'\nimport { ToggleToolLockedButton } from './ToggleToolLockedButton'\n\n/** @public */\nexport interface DefaultToolbarProps {\n\tchildren?: ReactNode\n}\n\n/**\n * The default toolbar for the editor. `children` defaults to the `DefaultToolbarContent` component.\n * Depending on the screen size, the children will overflow into a drop-down menu, with the most\n * recently active item from the overflow being shown in the main toolbar.\n *\n * @public\n * @react\n */\nexport const DefaultToolbar = memo(function DefaultToolbar({ children }: DefaultToolbarProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst breakpoint = useBreakpoint()\n\tconst isReadonlyMode = useReadonly()\n\tconst activeToolId = useValue('current tool id', () => editor.getCurrentToolId(), [editor])\n\n\tconst ref = useRef<HTMLDivElement>(null)\n\tusePassThroughWheelEvents(ref)\n\n\tconst { ActionsMenu, QuickActions } = useTldrawUiComponents()\n\n\tconst showQuickActions =\n\t\teditor.options.actionShortcutsLocation === 'menu'\n\t\t\t? false\n\t\t\t: editor.options.actionShortcutsLocation === 'toolbar'\n\t\t\t\t? true\n\t\t\t\t: breakpoint < PORTRAIT_BREAKPOINT.TABLET\n\n\treturn (\n\t\t<div ref={ref} className=\"tlui-main-toolbar\">\n\t\t\t<div className=\"tlui-main-toolbar__inner\">\n\t\t\t\t<div className=\"tlui-main-toolbar__left\">\n\t\t\t\t\t{!isReadonlyMode && (\n\t\t\t\t\t\t<div className=\"tlui-main-toolbar__extras\">\n\t\t\t\t\t\t\t{showQuickActions && (\n\t\t\t\t\t\t\t\t<TldrawUiToolbar\n\t\t\t\t\t\t\t\t\torientation=\"horizontal\"\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__extras__controls\"\n\t\t\t\t\t\t\t\t\tlabel={msg('actions-menu.title')}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{QuickActions && <QuickActions />}\n\t\t\t\t\t\t\t\t\t{ActionsMenu && <ActionsMenu />}\n\t\t\t\t\t\t\t\t</TldrawUiToolbar>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<ToggleToolLockedButton activeToolId={activeToolId} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<OverflowingToolbar>{children ?? <DefaultToolbarContent />}</OverflowingToolbar>\n\t\t\t\t</div>\n\t\t\t\t{breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (\n\t\t\t\t\t<div className=\"tlui-main-toolbar__tools\">\n\t\t\t\t\t\t<MobileStylePanel />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n"],
5
- "mappings": "AAoDQ,SAKkB,KALlB;AApDR,SAAS,WAAW,2BAA2B,gBAAgB;AAC/D,SAAoB,MAAM,cAAc;AACxC,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AACtC,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAehC,MAAM,iBAAiB,KAAK,SAASA,gBAAe,EAAE,SAAS,GAAwB;AAC7F,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,eAAe;AAC3B,QAAM,aAAa,cAAc;AACjC,QAAM,iBAAiB,YAAY;AACnC,QAAM,eAAe,SAAS,mBAAmB,MAAM,OAAO,iBAAiB,GAAG,CAAC,MAAM,CAAC;AAE1F,QAAM,MAAM,OAAuB,IAAI;AACvC,4BAA0B,GAAG;AAE7B,QAAM,EAAE,aAAa,aAAa,IAAI,sBAAsB;AAE5D,QAAM,mBACL,OAAO,QAAQ,4BAA4B,SACxC,QACA,OAAO,QAAQ,4BAA4B,YAC1C,OACA,aAAa,oBAAoB;AAEtC,SACC,oBAAC,SAAI,KAAU,WAAU,qBACxB,+BAAC,SAAI,WAAU,4BACd;AAAA,yBAAC,SAAI,WAAU,2BACb;AAAA,OAAC,kBACD,qBAAC,SAAI,WAAU,6BACb;AAAA,4BACA;AAAA,UAAC;AAAA;AAAA,YACA,aAAY;AAAA,YACZ,WAAU;AAAA,YACV,OAAO,IAAI,oBAAoB;AAAA,YAE9B;AAAA,8BAAgB,oBAAC,gBAAa;AAAA,cAC9B,eAAe,oBAAC,eAAY;AAAA;AAAA;AAAA,QAC9B;AAAA,QAED,oBAAC,0BAAuB,cAA4B;AAAA,SACrD;AAAA,MAED,oBAAC,sBAAoB,sBAAY,oBAAC,yBAAsB,GAAG;AAAA,OAC5D;AAAA,IACC,aAAa,oBAAoB,aAAa,CAAC,kBAC/C,oBAAC,SAAI,WAAU,4BACd,8BAAC,oBAAiB,GACnB;AAAA,KAEF,GACD;AAEF,CAAC;",
4
+ "sourcesContent": ["import { useEditor, usePassThroughWheelEvents, useValue } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { ReactNode, memo, useRef } from 'react'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { useTldrawUiComponents } from '../../context/components'\nimport { useReadonly } from '../../hooks/useReadonly'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { MobileStylePanel } from '../MobileStylePanel'\nimport { TldrawUiOrientationProvider } from '../primitives/layout'\nimport { TldrawUiToolbar } from '../primitives/TldrawUiToolbar'\nimport { DefaultToolbarContent } from './DefaultToolbarContent'\nimport { OverflowingToolbar } from './OverflowingToolbar'\nimport { ToggleToolLockedButton } from './ToggleToolLockedButton'\n\n/** @public */\nexport interface DefaultToolbarProps {\n\tchildren?: ReactNode\n\torientation?: 'horizontal' | 'vertical'\n\tminItems?: number\n\tminSizePx?: number\n\tmaxItems?: number\n\tmaxSizePx?: number\n}\n\n/**\n * The default toolbar for the editor. `children` defaults to the `DefaultToolbarContent` component.\n * Depending on the screen size, the children will overflow into a drop-down menu, with the most\n * recently active item from the overflow being shown in the main toolbar.\n *\n * @public\n * @react\n */\nexport const DefaultToolbar = memo(function DefaultToolbar({\n\tchildren,\n\torientation = 'horizontal',\n\tminItems = 4,\n\tminSizePx = 310,\n\tmaxItems = 8,\n\tmaxSizePx = 470,\n}: DefaultToolbarProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst breakpoint = useBreakpoint()\n\tconst isReadonlyMode = useReadonly()\n\tconst activeToolId = useValue('current tool id', () => editor.getCurrentToolId(), [editor])\n\n\tconst ref = useRef<HTMLDivElement>(null)\n\tusePassThroughWheelEvents(ref)\n\n\tconst { ActionsMenu, QuickActions } = useTldrawUiComponents()\n\n\tconst showQuickActions =\n\t\teditor.options.actionShortcutsLocation === 'menu'\n\t\t\t? false\n\t\t\t: editor.options.actionShortcutsLocation === 'toolbar'\n\t\t\t\t? true\n\t\t\t\t: breakpoint < PORTRAIT_BREAKPOINT.TABLET\n\n\treturn (\n\t\t<TldrawUiOrientationProvider\n\t\t\torientation={orientation}\n\t\t\ttooltipSide={orientation === 'horizontal' ? 'top' : 'right'}\n\t\t>\n\t\t\t<div\n\t\t\t\tref={ref}\n\t\t\t\tclassName={classNames('tlui-main-toolbar', `tlui-main-toolbar--${orientation}`)}\n\t\t\t>\n\t\t\t\t<div className=\"tlui-main-toolbar__inner\">\n\t\t\t\t\t<div className=\"tlui-main-toolbar__left\">\n\t\t\t\t\t\t{!isReadonlyMode && (\n\t\t\t\t\t\t\t<div className=\"tlui-main-toolbar__extras\">\n\t\t\t\t\t\t\t\t{showQuickActions && (\n\t\t\t\t\t\t\t\t\t<TldrawUiToolbar\n\t\t\t\t\t\t\t\t\t\torientation={orientation}\n\t\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__extras__controls\"\n\t\t\t\t\t\t\t\t\t\tlabel={msg('actions-menu.title')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{QuickActions && <QuickActions />}\n\t\t\t\t\t\t\t\t\t\t{ActionsMenu && <ActionsMenu />}\n\t\t\t\t\t\t\t\t\t</TldrawUiToolbar>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<ToggleToolLockedButton activeToolId={activeToolId} />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<OverflowingToolbar\n\t\t\t\t\t\t\torientation={orientation}\n\t\t\t\t\t\t\tsizingParentClassName=\"tlui-main-toolbar\"\n\t\t\t\t\t\t\tminItems={minItems}\n\t\t\t\t\t\t\tmaxItems={maxItems}\n\t\t\t\t\t\t\tminSizePx={minSizePx}\n\t\t\t\t\t\t\tmaxSizePx={maxSizePx}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{children ?? <DefaultToolbarContent />}\n\t\t\t\t\t\t</OverflowingToolbar>\n\t\t\t\t\t</div>\n\t\t\t\t\t{breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (\n\t\t\t\t\t\t<div className=\"tlui-main-toolbar__tools tlui-main-toolbar__mobile-style-panel\">\n\t\t\t\t\t\t\t<MobileStylePanel />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</TldrawUiOrientationProvider>\n\t)\n})\n"],
5
+ "mappings": "AAyES,SAKkB,KALlB;AAzET,SAAS,WAAW,2BAA2B,gBAAgB;AAC/D,OAAO,gBAAgB;AACvB,SAAoB,MAAM,cAAc;AACxC,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AACtC,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAoBhC,MAAM,iBAAiB,KAAK,SAASA,gBAAe;AAAA,EAC1D;AAAA,EACA,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AACb,GAAwB;AACvB,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,eAAe;AAC3B,QAAM,aAAa,cAAc;AACjC,QAAM,iBAAiB,YAAY;AACnC,QAAM,eAAe,SAAS,mBAAmB,MAAM,OAAO,iBAAiB,GAAG,CAAC,MAAM,CAAC;AAE1F,QAAM,MAAM,OAAuB,IAAI;AACvC,4BAA0B,GAAG;AAE7B,QAAM,EAAE,aAAa,aAAa,IAAI,sBAAsB;AAE5D,QAAM,mBACL,OAAO,QAAQ,4BAA4B,SACxC,QACA,OAAO,QAAQ,4BAA4B,YAC1C,OACA,aAAa,oBAAoB;AAEtC,SACC;AAAA,IAAC;AAAA;AAAA,MACA;AAAA,MACA,aAAa,gBAAgB,eAAe,QAAQ;AAAA,MAEpD;AAAA,QAAC;AAAA;AAAA,UACA;AAAA,UACA,WAAW,WAAW,qBAAqB,sBAAsB,WAAW,EAAE;AAAA,UAE9E,+BAAC,SAAI,WAAU,4BACd;AAAA,iCAAC,SAAI,WAAU,2BACb;AAAA,eAAC,kBACD,qBAAC,SAAI,WAAU,6BACb;AAAA,oCACA;AAAA,kBAAC;AAAA;AAAA,oBACA;AAAA,oBACA,WAAU;AAAA,oBACV,OAAO,IAAI,oBAAoB;AAAA,oBAE9B;AAAA,sCAAgB,oBAAC,gBAAa;AAAA,sBAC9B,eAAe,oBAAC,eAAY;AAAA;AAAA;AAAA,gBAC9B;AAAA,gBAED,oBAAC,0BAAuB,cAA4B;AAAA,iBACrD;AAAA,cAED;AAAA,gBAAC;AAAA;AAAA,kBACA;AAAA,kBACA,uBAAsB;AAAA,kBACtB;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBAEC,sBAAY,oBAAC,yBAAsB;AAAA;AAAA,cACrC;AAAA,eACD;AAAA,YACC,aAAa,oBAAoB,aAAa,CAAC,kBAC/C,oBAAC,SAAI,WAAU,kEACd,8BAAC,oBAAiB,GACnB;AAAA,aAEF;AAAA;AAAA,MACD;AAAA;AAAA,EACD;AAEF,CAAC;",
6
6
  "names": ["DefaultToolbar"]
7
7
  }
@@ -1,6 +1,8 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import {
3
3
  activeElementShouldCaptureKeys,
4
+ assert,
5
+ modulate,
4
6
  preventDefault,
5
7
  tlmenus,
6
8
  useEditor,
@@ -8,7 +10,7 @@ import {
8
10
  useUniqueSafeId
9
11
  } from "@tldraw/editor";
10
12
  import classNames from "classnames";
11
- import { createContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
13
+ import { createContext, useEffect, useLayoutEffect, useRef, useState } from "react";
12
14
  import { PORTRAIT_BREAKPOINT } from "../../constants.mjs";
13
15
  import { useBreakpoint } from "../../context/breakpoints.mjs";
14
16
  import { areShortcutsDisabled } from "../../hooks/useKeyboardShortcuts.mjs";
@@ -20,7 +22,7 @@ import {
20
22
  TldrawUiPopoverTrigger
21
23
  } from "../primitives/TldrawUiPopover.mjs";
22
24
  import { TldrawUiToolbar, TldrawUiToolbarButton } from "../primitives/TldrawUiToolbar.mjs";
23
- import { TldrawUiRow } from "../primitives/layout.mjs";
25
+ import { TldrawUiColumn, TldrawUiRow } from "../primitives/layout.mjs";
24
26
  import { TldrawUiMenuContextProvider } from "../primitives/menus/TldrawUiMenuContext.mjs";
25
27
  const IsInOverflowContext = createContext(false);
26
28
  const NUMBERED_SHORTCUT_KEYS = {
@@ -35,52 +37,130 @@ const NUMBERED_SHORTCUT_KEYS = {
35
37
  "9": 8,
36
38
  "0": 9
37
39
  };
38
- function OverflowingToolbar({ children }) {
40
+ function OverflowingToolbar({
41
+ children,
42
+ orientation,
43
+ sizingParentClassName,
44
+ minItems,
45
+ minSizePx,
46
+ maxItems,
47
+ maxSizePx
48
+ }) {
39
49
  const editor = useEditor();
40
50
  const id = useUniqueSafeId();
41
51
  const breakpoint = useBreakpoint();
42
52
  const msg = useTranslation();
43
53
  const rButtons = useRef([]);
44
54
  const [isOpen, setIsOpen] = useState(false);
45
- const overflowIndex = Math.min(8, 5 + breakpoint);
46
- const [totalItems, setTotalItems] = useState(0);
47
55
  const mainToolsRef = useRef(null);
56
+ const [overflowTools, setOverflowTools] = useState(null);
48
57
  const [lastActiveOverflowItem, setLastActiveOverflowItem] = useState(null);
49
- const css = useMemo(() => {
50
- const activeCss = lastActiveOverflowItem ? `:not([data-value="${lastActiveOverflowItem}"])` : "";
51
- return `
52
- #${id}_main > *:nth-of-type(n + ${overflowIndex + (lastActiveOverflowItem ? 1 : 2)}):not([data-radix-popper-content-wrapper])${activeCss} {
53
- display: none;
54
- }
55
- #${id}_more > *:nth-of-type(-n + ${overflowIndex}):not([data-radix-popper-content-wrapper]) {
56
- display: none;
57
- }
58
- `;
59
- }, [lastActiveOverflowItem, id, overflowIndex]);
58
+ const [shouldShowOverflow, setShouldShowOverflow] = useState(false);
60
59
  const onDomUpdate = useEvent(() => {
61
60
  if (!mainToolsRef.current) return;
62
- const children2 = Array.from(mainToolsRef.current.children);
63
- setTotalItems(children2.length);
64
- const lastActiveElementIdx = children2.findIndex(
65
- (el) => el.getAttribute("data-value") === lastActiveOverflowItem
66
- );
67
- if (lastActiveElementIdx <= overflowIndex) {
68
- setLastActiveOverflowItem(null);
61
+ const sizeProp = orientation === "horizontal" ? "offsetWidth" : "offsetHeight";
62
+ const mainItems = collectItems(mainToolsRef.current.children);
63
+ const overflowItems = overflowTools ? collectItems(overflowTools.children) : null;
64
+ function collectItems(collection) {
65
+ const items = [];
66
+ for (const child of collection) {
67
+ if (child.classList.contains("tlui-main-toolbar__group")) {
68
+ items.push({
69
+ type: "group",
70
+ items: collectItems(child.children),
71
+ element: child
72
+ });
73
+ } else {
74
+ items.push({ type: "item", element: child });
75
+ }
76
+ }
77
+ return items;
69
78
  }
70
- const activeElementIdx = Array.from(mainToolsRef.current.children).findIndex(
71
- (el) => el.getAttribute("aria-pressed") === "true"
79
+ const sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName);
80
+ const size = sizingParent[sizeProp];
81
+ const itemsToShow = Math.floor(
82
+ modulate(size, [minSizePx, maxSizePx], [minItems, maxItems], true)
72
83
  );
73
- if (activeElementIdx === -1) return;
74
- if (activeElementIdx >= overflowIndex) {
75
- setLastActiveOverflowItem(children2[activeElementIdx].getAttribute("data-value"));
76
- }
77
- rButtons.current = Array.from(mainToolsRef.current?.children ?? []).filter(
78
- (el) => {
79
- if (!(el instanceof HTMLElement)) return false;
80
- if (el.tagName.toLowerCase() !== "button") return false;
81
- return !!(el.offsetWidth || el.offsetHeight);
84
+ let mainItemCount = 0;
85
+ let newActiveOverflowItem = null;
86
+ let shouldInvalidateLastActiveOverflowItem = false;
87
+ const numberedButtons = [];
88
+ function visitItems(mainItems2, overflowItems2) {
89
+ if (overflowItems2) assert(mainItems2.length === overflowItems2.length);
90
+ let didShowAnyInMain = false;
91
+ let didShowAnyInOverflow2 = false;
92
+ for (let i = 0; i < mainItems2.length; i++) {
93
+ const mainItem = mainItems2[i];
94
+ const overflowItem = overflowItems2?.[i];
95
+ if (mainItem.type === "item") {
96
+ const isLastActiveOverflowItem = mainItem.element.getAttribute("data-value") === lastActiveOverflowItem;
97
+ let shouldShowInMain;
98
+ if (lastActiveOverflowItem) {
99
+ shouldShowInMain = mainItemCount < itemsToShow || isLastActiveOverflowItem;
100
+ } else {
101
+ shouldShowInMain = mainItemCount <= itemsToShow;
102
+ }
103
+ const shouldShowInOverflow = mainItemCount >= itemsToShow;
104
+ didShowAnyInMain ||= shouldShowInMain;
105
+ didShowAnyInOverflow2 ||= shouldShowInOverflow;
106
+ setAttribute(
107
+ mainItem.element,
108
+ "data-toolbar-visible",
109
+ shouldShowInMain ? "true" : "false"
110
+ );
111
+ if (overflowItem) {
112
+ assert(overflowItem.type === "item");
113
+ setAttribute(
114
+ overflowItem.element,
115
+ "data-toolbar-visible",
116
+ shouldShowInOverflow ? "true" : "false"
117
+ );
118
+ }
119
+ if (shouldShowInOverflow && mainItem.element.getAttribute("aria-pressed") === "true") {
120
+ newActiveOverflowItem = mainItem.element.getAttribute("data-value");
121
+ }
122
+ if (shouldShowInMain && mainItem.element.tagName === "BUTTON") {
123
+ numberedButtons.push(mainItem.element);
124
+ }
125
+ if (!shouldShowInOverflow && isLastActiveOverflowItem) {
126
+ shouldInvalidateLastActiveOverflowItem = true;
127
+ }
128
+ mainItemCount++;
129
+ } else {
130
+ let result, overflowGroup;
131
+ if (overflowItem) {
132
+ assert(overflowItem.type === "group");
133
+ overflowGroup = overflowItem;
134
+ result = visitItems(mainItem.items, overflowGroup.items);
135
+ } else {
136
+ result = visitItems(mainItem.items, null);
137
+ }
138
+ didShowAnyInMain ||= result.didShowAnyInMain;
139
+ didShowAnyInOverflow2 ||= result.didShowAnyInOverflow;
140
+ setAttribute(
141
+ mainItem.element,
142
+ "data-toolbar-visible",
143
+ result.didShowAnyInMain ? "true" : "false"
144
+ );
145
+ if (overflowGroup) {
146
+ setAttribute(
147
+ overflowGroup.element,
148
+ "data-toolbar-visible",
149
+ result.didShowAnyInOverflow ? "true" : "false"
150
+ );
151
+ }
152
+ }
82
153
  }
83
- );
154
+ return { didShowAnyInMain, didShowAnyInOverflow: didShowAnyInOverflow2 };
155
+ }
156
+ const { didShowAnyInOverflow } = visitItems(mainItems, overflowItems);
157
+ setShouldShowOverflow(didShowAnyInOverflow);
158
+ if (newActiveOverflowItem) {
159
+ setLastActiveOverflowItem(newActiveOverflowItem);
160
+ } else if (shouldInvalidateLastActiveOverflowItem) {
161
+ setLastActiveOverflowItem(null);
162
+ }
163
+ rButtons.current = numberedButtons;
84
164
  });
85
165
  useLayoutEffect(() => {
86
166
  onDomUpdate();
@@ -91,20 +171,26 @@ function OverflowingToolbar({ children }) {
91
171
  mutationObserver.observe(mainToolsRef.current, {
92
172
  childList: true,
93
173
  subtree: true,
94
- attributeFilter: ["data-value", "aria-pressed"]
174
+ attributes: true,
175
+ characterData: true
95
176
  });
177
+ const sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName);
178
+ const resizeObserver = new ResizeObserver(onDomUpdate);
179
+ resizeObserver.observe(sizingParent);
96
180
  return () => {
97
181
  mutationObserver.disconnect();
182
+ resizeObserver.disconnect();
98
183
  };
99
- }, [onDomUpdate]);
184
+ }, [onDomUpdate, sizingParentClassName]);
100
185
  useEffect(() => {
101
186
  if (!editor.options.enableToolbarKeyboardShortcuts) return;
102
187
  function handleKeyDown(event) {
103
188
  if (areShortcutsDisabled(editor) || activeElementShouldCaptureKeys(
104
189
  true
105
190
  /* allow buttons */
106
- ))
191
+ )) {
107
192
  return;
193
+ }
108
194
  if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return;
109
195
  const index = NUMBERED_SHORTCUT_KEYS[event.key];
110
196
  if (typeof index === "number") {
@@ -118,52 +204,78 @@ function OverflowingToolbar({ children }) {
118
204
  };
119
205
  }, [editor]);
120
206
  const popoverId = "toolbar overflow";
121
- return /* @__PURE__ */ jsxs(Fragment, { children: [
122
- /* @__PURE__ */ jsx("style", { nonce: editor.options.nonce, children: css }),
123
- /* @__PURE__ */ jsxs(
124
- TldrawUiToolbar,
125
- {
126
- orientation: "horizontal",
127
- className: classNames("tlui-main-toolbar__tools", {
128
- "tlui-main-toolbar__tools__mobile": breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
129
- }),
130
- label: msg("tool-panel.title"),
131
- children: [
132
- /* @__PURE__ */ jsx(TldrawUiRow, { id: `${id}_main`, ref: mainToolsRef, children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar", sourceId: "toolbar", children }) }),
133
- totalItems > overflowIndex + 1 && /* @__PURE__ */ jsx(IsInOverflowContext.Provider, { value: true, children: /* @__PURE__ */ jsxs(TldrawUiPopover, { id: popoverId, open: isOpen, onOpenChange: setIsOpen, children: [
134
- /* @__PURE__ */ jsx(TldrawUiPopoverTrigger, { children: /* @__PURE__ */ jsx(
135
- TldrawUiToolbarButton,
136
- {
137
- title: msg("tool-panel.more"),
138
- type: "tool",
139
- className: "tlui-main-toolbar__overflow",
140
- "data-testid": "tools.more-button",
141
- children: /* @__PURE__ */ jsx(TldrawUiButtonIcon, { icon: "chevron-up" })
142
- }
143
- ) }),
144
- /* @__PURE__ */ jsx(TldrawUiPopoverContent, { side: "top", align: "center", children: /* @__PURE__ */ jsx(
145
- TldrawUiToolbar,
146
- {
147
- orientation: "grid",
148
- "data-testid": "tools.more-content",
149
- label: msg("tool-panel.more"),
150
- id: `${id}_more`,
151
- onClick: () => {
152
- tlmenus.deleteOpenMenu(popoverId, editor.contextId);
153
- setIsOpen(false);
154
- },
155
- children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar-overflow", sourceId: "toolbar", children })
156
- }
157
- ) })
158
- ] }) })
159
- ]
160
- }
161
- )
162
- ] });
207
+ const Layout = orientation === "horizontal" ? TldrawUiRow : TldrawUiColumn;
208
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
209
+ TldrawUiToolbar,
210
+ {
211
+ orientation,
212
+ className: classNames("tlui-main-toolbar__tools", {
213
+ "tlui-main-toolbar__tools__mobile": breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
214
+ }),
215
+ label: msg("tool-panel.title"),
216
+ children: [
217
+ /* @__PURE__ */ jsx(Layout, { id: `${id}_main`, ref: mainToolsRef, children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar", sourceId: "toolbar", children }) }),
218
+ shouldShowOverflow && /* @__PURE__ */ jsx(IsInOverflowContext.Provider, { value: true, children: /* @__PURE__ */ jsxs(TldrawUiPopover, { id: popoverId, open: isOpen, onOpenChange: setIsOpen, children: [
219
+ /* @__PURE__ */ jsx(TldrawUiPopoverTrigger, { children: /* @__PURE__ */ jsx(
220
+ TldrawUiToolbarButton,
221
+ {
222
+ title: msg("tool-panel.more"),
223
+ type: "tool",
224
+ className: "tlui-main-toolbar__overflow",
225
+ "data-testid": "tools.more-button",
226
+ children: /* @__PURE__ */ jsx(
227
+ TldrawUiButtonIcon,
228
+ {
229
+ icon: orientation === "horizontal" ? "chevron-up" : "chevron-right"
230
+ }
231
+ )
232
+ }
233
+ ) }),
234
+ /* @__PURE__ */ jsx(
235
+ TldrawUiPopoverContent,
236
+ {
237
+ side: orientation === "horizontal" ? "top" : "right",
238
+ align: orientation === "horizontal" ? "center" : "end",
239
+ children: /* @__PURE__ */ jsx(
240
+ TldrawUiToolbar,
241
+ {
242
+ orientation: "grid",
243
+ className: "tlui-main-toolbar__overflow-content",
244
+ ref: setOverflowTools,
245
+ "data-testid": "tools.more-content",
246
+ label: msg("tool-panel.more"),
247
+ id: `${id}_more`,
248
+ onClick: () => {
249
+ tlmenus.deleteOpenMenu(popoverId, editor.contextId);
250
+ setIsOpen(false);
251
+ },
252
+ children: /* @__PURE__ */ jsx(TldrawUiMenuContextProvider, { type: "toolbar-overflow", sourceId: "toolbar", children })
253
+ }
254
+ )
255
+ }
256
+ )
257
+ ] }) })
258
+ ]
259
+ }
260
+ ) });
163
261
  }
164
262
  const isActiveTLUiToolItem = (item, activeToolId, geoState) => {
165
263
  return item.meta?.geo ? activeToolId === "geo" && geoState === item.meta?.geo : activeToolId === item.id;
166
264
  };
265
+ function findParentWithClassName(startingElement, className) {
266
+ let element = startingElement;
267
+ while (element) {
268
+ if (element.classList.contains(className)) {
269
+ return element;
270
+ }
271
+ element = element.parentElement;
272
+ }
273
+ throw new Error("Could not find parent with class name " + className);
274
+ }
275
+ function setAttribute(element, name, value) {
276
+ if (element.getAttribute(name) === value) return;
277
+ element.setAttribute(name, value);
278
+ }
167
279
  export {
168
280
  IsInOverflowContext,
169
281
  OverflowingToolbar,