tldraw 3.16.0-canary.faec5de49906 → 3.16.0-canary.fe4babe9c1ad

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 (135) hide show
  1. package/dist-cjs/index.d.ts +7 -5
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/defaultExternalContentHandlers.js +10 -0
  4. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  5. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +4 -4
  6. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  7. package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js +2 -2
  8. package/dist-cjs/lib/shapes/frame/components/FrameLabelInput.js.map +2 -2
  9. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +4 -4
  10. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
  11. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js +3 -2
  12. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  13. package/dist-cjs/lib/shapes/text/PlainTextArea.js +2 -2
  14. package/dist-cjs/lib/shapes/text/PlainTextArea.js.map +2 -2
  15. package/dist-cjs/lib/shapes/text/RichTextArea.js +3 -3
  16. package/dist-cjs/lib/shapes/text/RichTextArea.js.map +2 -2
  17. package/dist-cjs/lib/ui/components/A11y.js +1 -1
  18. package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
  19. package/dist-cjs/lib/ui/components/LanguageMenu.js +1 -0
  20. package/dist-cjs/lib/ui/components/LanguageMenu.js.map +2 -2
  21. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +1 -0
  22. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
  23. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js +1 -1
  24. package/dist-cjs/lib/ui/components/PageMenu/DefaultPageMenu.js.map +2 -2
  25. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +11 -2
  26. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +2 -2
  27. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +3 -2
  28. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  29. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +5 -4
  30. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  31. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +1 -1
  32. package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +2 -2
  33. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +6 -2
  34. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
  35. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +1 -1
  36. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +1 -1
  37. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +5 -3
  38. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
  39. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +1 -0
  40. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  41. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +14 -2
  42. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  43. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +3 -0
  44. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
  45. package/dist-cjs/lib/ui/context/actions.js +6 -0
  46. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  47. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +1 -1
  48. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
  49. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  50. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  51. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  52. package/dist-cjs/lib/ui/version.js +3 -3
  53. package/dist-cjs/lib/ui/version.js.map +1 -1
  54. package/dist-esm/index.d.mts +7 -5
  55. package/dist-esm/index.mjs +1 -1
  56. package/dist-esm/lib/defaultExternalContentHandlers.mjs +10 -0
  57. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  58. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +5 -5
  59. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  60. package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs +3 -3
  61. package/dist-esm/lib/shapes/frame/components/FrameLabelInput.mjs.map +2 -2
  62. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +5 -5
  63. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
  64. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs +4 -3
  65. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  66. package/dist-esm/lib/shapes/text/PlainTextArea.mjs +3 -3
  67. package/dist-esm/lib/shapes/text/PlainTextArea.mjs.map +2 -2
  68. package/dist-esm/lib/shapes/text/RichTextArea.mjs +3 -4
  69. package/dist-esm/lib/shapes/text/RichTextArea.mjs.map +2 -2
  70. package/dist-esm/lib/ui/components/A11y.mjs +2 -2
  71. package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
  72. package/dist-esm/lib/ui/components/LanguageMenu.mjs +1 -0
  73. package/dist-esm/lib/ui/components/LanguageMenu.mjs.map +2 -2
  74. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +1 -0
  75. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
  76. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs +2 -2
  77. package/dist-esm/lib/ui/components/PageMenu/DefaultPageMenu.mjs.map +2 -2
  78. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +11 -2
  79. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +2 -2
  80. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +3 -2
  81. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  82. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +5 -4
  83. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  84. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +1 -1
  85. package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +2 -2
  86. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +6 -2
  87. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
  88. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +2 -2
  89. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +1 -1
  90. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +6 -4
  91. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
  92. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +1 -0
  93. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  94. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +14 -2
  95. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  96. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +3 -0
  97. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
  98. package/dist-esm/lib/ui/context/actions.mjs +6 -0
  99. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  100. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +2 -2
  101. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
  102. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  103. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  104. package/dist-esm/lib/ui/version.mjs +3 -3
  105. package/dist-esm/lib/ui/version.mjs.map +1 -1
  106. package/package.json +3 -3
  107. package/src/lib/defaultExternalContentHandlers.ts +14 -0
  108. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +2 -2
  109. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +5 -5
  110. package/src/lib/shapes/frame/components/FrameLabelInput.tsx +3 -3
  111. package/src/lib/shapes/shared/HyperlinkButton.tsx +5 -5
  112. package/src/lib/shapes/shared/useEditablePlainText.ts +5 -3
  113. package/src/lib/shapes/text/PlainTextArea.tsx +3 -3
  114. package/src/lib/shapes/text/RichTextArea.tsx +3 -4
  115. package/src/lib/ui/components/A11y.tsx +2 -2
  116. package/src/lib/ui/components/LanguageMenu.tsx +1 -0
  117. package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +1 -0
  118. package/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx +2 -2
  119. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +9 -2
  120. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +4 -3
  121. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +6 -5
  122. package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +1 -1
  123. package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +9 -2
  124. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +2 -2
  125. package/src/lib/ui/components/primitives/TldrawUiInput.tsx +6 -3
  126. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +2 -1
  127. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +16 -2
  128. package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +4 -0
  129. package/src/lib/ui/context/actions.tsx +13 -0
  130. package/src/lib/ui/hooks/useClipboardEvents.ts +2 -2
  131. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  132. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  133. package/src/lib/ui/version.ts +3 -3
  134. package/src/lib/ui.css +10 -0
  135. package/tldraw.css +10 -0
@@ -3,9 +3,9 @@ import {
3
3
  TLShapeId,
4
4
  TLUnknownShape,
5
5
  getPointerInfo,
6
+ markEventAsHandled,
6
7
  noop,
7
8
  preventDefault,
8
- stopEventPropagation,
9
9
  tlenv,
10
10
  useEditor,
11
11
  useValue,
@@ -136,7 +136,7 @@ export function useEditableTextCommon(shapeId: TLShapeId) {
136
136
  shape: editor.getShape(shapeId)!,
137
137
  })
138
138
 
139
- stopEventPropagation(e) // we need to prevent blurring the input
139
+ e.stopPropagation() // we need to prevent blurring the input
140
140
  },
141
141
  [editor, shapeId]
142
142
  )
@@ -157,11 +157,13 @@ export function useEditableTextCommon(shapeId: TLShapeId) {
157
157
  [editor, shapeId]
158
158
  )
159
159
 
160
+ const handleDoubleClick: (e: React.MouseEvent) => void = markEventAsHandled
161
+
160
162
  return {
161
163
  handleFocus: noop,
162
164
  handleBlur: noop,
163
165
  handleInputPointerDown,
164
- handleDoubleClick: stopEventPropagation,
166
+ handleDoubleClick,
165
167
  handlePaste,
166
168
  isEditing,
167
169
  isReadyForEditing,
@@ -1,4 +1,4 @@
1
- import { preventDefault, stopEventPropagation } from '@tldraw/editor'
1
+ import { markEventAsHandled, preventDefault } from '@tldraw/editor'
2
2
  import React from 'react'
3
3
  import { TextAreaProps } from './RichTextArea'
4
4
 
@@ -46,8 +46,8 @@ export const PlainTextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps
46
46
  onChange={onChange}
47
47
  onKeyDown={(e) => handleKeyDown(e.nativeEvent)}
48
48
  onBlur={handleBlur}
49
- onTouchEnd={stopEventPropagation}
50
- onContextMenu={isEditing ? stopEventPropagation : undefined}
49
+ onTouchEnd={markEventAsHandled}
50
+ onContextMenu={isEditing ? (e) => e.stopPropagation() : undefined}
51
51
  onPointerDown={handleInputPointerDown}
52
52
  onPaste={handlePaste}
53
53
  onDoubleClick={handleDoubleClick}
@@ -10,7 +10,6 @@ import {
10
10
  TLRichText,
11
11
  TLShapeId,
12
12
  preventDefault,
13
- stopEventPropagation,
14
13
  useEditor,
15
14
  useEvent,
16
15
  useUniqueSafeId,
@@ -233,13 +232,13 @@ export const RichTextArea = React.forwardRef<HTMLDivElement, TextAreaProps>(func
233
232
  tabIndex={-1}
234
233
  data-testid="rich-text-area"
235
234
  className="tl-rich-text tl-text tl-text-input"
236
- onContextMenu={isEditing ? stopEventPropagation : undefined}
235
+ onContextMenu={isEditing ? (e) => e.stopPropagation() : undefined}
237
236
  // N.B. When PointerStateExtension was introduced, this was moved there.
238
237
  // However, that caused selecting over list items to break.
239
238
  // The handleDOMEvents in TipTap don't seem to support the pointerDownCapture event.
240
- onPointerDownCapture={stopEventPropagation}
239
+ onPointerDownCapture={(e) => e.stopPropagation()}
241
240
  // This onTouchEnd is important for Android to be able to change selection on text.
242
- onTouchEnd={stopEventPropagation}
241
+ onTouchEnd={(e) => e.stopPropagation()}
243
242
  // On FF, there's a behavior where dragging a selection will grab that selection into
244
243
  // the drag event. However, once the drag is over, and you select away from the textarea,
245
244
  // starting a drag over the textarea will restart a selection drag instead of a shape drag.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  debugFlags,
3
3
  Editor,
4
- stopEventPropagation,
4
+ markEventAsHandled,
5
5
  TLGeoShape,
6
6
  TLShapeId,
7
7
  unsafe__withoutCapture,
@@ -23,7 +23,7 @@ export function SkipToMainContent() {
23
23
 
24
24
  const handleNavigateToFirstShape = useCallback(
25
25
  (e: MouseEvent | KeyboardEvent) => {
26
- stopEventPropagation(e)
26
+ markEventAsHandled(e)
27
27
  button.current?.blur()
28
28
  const shapes = editor.getCurrentPageShapesInReadingOrder()
29
29
  if (!shapes.length) return
@@ -18,6 +18,7 @@ export function LanguageMenu() {
18
18
  {LANGUAGES.map(({ locale, label }) => (
19
19
  <TldrawUiMenuCheckboxItem
20
20
  id={`language-${locale}`}
21
+ lang={locale}
21
22
  key={locale}
22
23
  title={locale}
23
24
  label={label}
@@ -204,6 +204,7 @@ export function DefaultMinimap() {
204
204
  <canvas
205
205
  role="img"
206
206
  aria-label={msg('navigation-zone.minimap')}
207
+ data-testid="minimap.canvas"
207
208
  ref={rCanvas}
208
209
  className="tlui-minimap__canvas"
209
210
  onDoubleClick={onDoubleClick}
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PageRecordType,
3
3
  TLPageId,
4
+ markEventAsHandled,
4
5
  releasePointerCapture,
5
6
  setPointerCapture,
6
- stopEventPropagation,
7
7
  tlenv,
8
8
  useEditor,
9
9
  useValue,
@@ -451,7 +451,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
451
451
  if (e.key === 'Enter') {
452
452
  if (page.id === currentPage.id) {
453
453
  toggleEditing()
454
- stopEventPropagation(e)
454
+ markEventAsHandled(e)
455
455
  }
456
456
  }
457
457
  }}
@@ -137,6 +137,7 @@ export const StylePanelButtonPicker = memo(function StylePanelButtonPicker<T ext
137
137
  >
138
138
  <Layout>
139
139
  {items.map((item) => {
140
+ const isActive = value.type === 'shared' && value.value === item.value
140
141
  const label =
141
142
  title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
142
143
  return (
@@ -145,10 +146,16 @@ export const StylePanelButtonPicker = memo(function StylePanelButtonPicker<T ext
145
146
  key={item.value}
146
147
  data-id={item.value}
147
148
  data-testid={`style.${uiType}.${item.value}`}
148
- aria-label={label}
149
+ aria-label={label + (isActive ? ` (${msg('style-panel.selected')})` : '')}
150
+ tooltip={
151
+ <>
152
+ <div>{label}</div>
153
+ {isActive ? <div>({msg('style-panel.selected')})</div> : null}
154
+ </>
155
+ }
149
156
  value={item.value}
150
157
  data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'}
151
- data-isactive={value.type === 'shared' && value.value === item.value}
158
+ data-isactive={isActive}
152
159
  title={label}
153
160
  style={
154
161
  style === (DefaultColorStyle as StyleProp<unknown>)
@@ -2,9 +2,9 @@ import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
5
+ import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
5
6
  import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
6
7
  import { TldrawUiInput } from '../primitives/TldrawUiInput'
7
- import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
8
8
 
9
9
  /** @public */
10
10
  export interface AltTextEditorProps {
@@ -70,13 +70,14 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
70
70
  data-testid="media-toolbar.alt-text-input"
71
71
  value={altText}
72
72
  placeholder={msg('tool.media-alt-text-desc')}
73
+ aria-label={msg('tool.media-alt-text-desc')}
73
74
  onValueChange={handleValueChange}
74
75
  onComplete={handleComplete}
75
76
  onCancel={handleAltTextCancel}
76
77
  disabled={isReadonly}
77
78
  />
78
79
  {!isReadonly && (
79
- <TldrawUiToolbarButton
80
+ <TldrawUiButton
80
81
  title={msg('tool.media-alt-text-confirm')}
81
82
  data-testid="tool.media-alt-text-confirm"
82
83
  type="icon"
@@ -84,7 +85,7 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
84
85
  onClick={handleConfirm}
85
86
  >
86
87
  <TldrawUiButtonIcon small icon="check" />
87
- </TldrawUiToolbarButton>
88
+ </TldrawUiButton>
88
89
  )}
89
90
  </>
90
91
  )
@@ -2,9 +2,9 @@ import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'
2
2
  import { useEffect, useRef, useState } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
5
+ import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
5
6
  import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
6
7
  import { TldrawUiInput } from '../primitives/TldrawUiInput'
7
- import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
8
8
 
9
9
  /** @public */
10
10
  export interface LinkEditorProps {
@@ -75,8 +75,9 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
75
75
  onComplete={handleLinkComplete}
76
76
  onCancel={handleLinkCancel}
77
77
  placeholder="example.com"
78
+ aria-label="example.com"
78
79
  />
79
- <TldrawUiToolbarButton
80
+ <TldrawUiButton
80
81
  className="tlui-rich-text__toolbar-link-visit"
81
82
  title={msg('tool.rich-text-link-visit')}
82
83
  type="icon"
@@ -85,8 +86,8 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
85
86
  disabled={!value}
86
87
  >
87
88
  <TldrawUiButtonIcon small icon="external-link" />
88
- </TldrawUiToolbarButton>
89
- <TldrawUiToolbarButton
89
+ </TldrawUiButton>
90
+ <TldrawUiButton
90
91
  className="tlui-rich-text__toolbar-link-remove"
91
92
  title={msg('tool.rich-text-link-remove')}
92
93
  data-testid="rich-text.link-remove"
@@ -95,7 +96,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
95
96
  onClick={handleRemoveLink}
96
97
  >
97
98
  <TldrawUiButtonIcon small icon="trash" />
98
- </TldrawUiToolbarButton>
99
+ </TldrawUiButton>
99
100
  </>
100
101
  )
101
102
  }
@@ -101,7 +101,7 @@ export function OverflowingToolbar({
101
101
  items: collectItems(child.children),
102
102
  element: child as HTMLElement,
103
103
  })
104
- } else {
104
+ } else if (!child.hasAttribute('data-radix-popper-content-wrapper')) {
105
105
  items.push({ type: 'item', element: child as HTMLElement })
106
106
  }
107
107
  }
@@ -1,8 +1,10 @@
1
1
  import { useEditor, useValue } from '@tldraw/editor'
2
2
  import classNames from 'classnames'
3
3
  import { PORTRAIT_BREAKPOINT } from '../../constants'
4
+ import { useActions } from '../../context/actions'
4
5
  import { useBreakpoint } from '../../context/breakpoints'
5
6
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
7
+ import { kbdStr } from '../../kbd-utils'
6
8
  import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
7
9
  import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
8
10
  import { TldrawUiTooltip } from '../primitives/TldrawUiTooltip'
@@ -17,6 +19,7 @@ export function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonP
17
19
  const editor = useEditor()
18
20
  const breakpoint = useBreakpoint()
19
21
  const msg = useTranslation()
22
+ const actions = useActions()
20
23
 
21
24
  const isToolLocked = useValue('is tool locked', () => editor.getInstanceState().isToolLocked, [
22
25
  editor,
@@ -25,11 +28,15 @@ export function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonP
25
28
 
26
29
  if (!activeToolId || !tool.isLockable) return null
27
30
 
31
+ const toggleLockAction = actions['toggle-tool-lock']
32
+ const tooltipContent = toggleLockAction?.kbd
33
+ ? `${msg('action.toggle-tool-lock')} ${kbdStr(toggleLockAction.kbd)}`
34
+ : msg('action.toggle-tool-lock')
35
+
28
36
  return (
29
- <TldrawUiTooltip content={msg('action.toggle-tool-lock')}>
37
+ <TldrawUiTooltip content={tooltipContent}>
30
38
  <TldrawUiButton
31
39
  type="normal"
32
- title={msg('action.toggle-tool-lock')}
33
40
  data-testid="tool-lock"
34
41
  className={classNames('tlui-main-toolbar__lock-button', {
35
42
  'tlui-main-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
@@ -3,8 +3,8 @@ import {
3
3
  Box,
4
4
  clamp,
5
5
  Editor,
6
+ markEventAsHandled,
6
7
  react,
7
- stopEventPropagation,
8
8
  useAtom,
9
9
  useEditor,
10
10
  usePassThroughMouseOverEvents,
@@ -170,7 +170,7 @@ export const TldrawUiContextualToolbar = ({
170
170
  data-visible={false}
171
171
  data-testid="contextual-toolbar"
172
172
  className={classNames('tlui-contextual-toolbar', className)}
173
- onPointerDown={stopEventPropagation}
173
+ onPointerDown={markEventAsHandled}
174
174
  >
175
175
  <TldrawUiToolbar
176
176
  orientation="horizontal"
@@ -1,4 +1,4 @@
1
- import { stopEventPropagation, tlenv, tltime, useMaybeEditor } from '@tldraw/editor'
1
+ import { tlenv, tltime, useMaybeEditor } from '@tldraw/editor'
2
2
  import classNames from 'classnames'
3
3
  import * as React from 'react'
4
4
  import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
@@ -35,6 +35,7 @@ export interface TLUiInputProps {
35
35
  shouldManuallyMaintainScrollPositionWhenFocused?: boolean
36
36
  value?: string
37
37
  'data-testid'?: string
38
+ 'aria-label'?: string
38
39
  }
39
40
 
40
41
  /** @public @react */
@@ -60,6 +61,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
60
61
  value,
61
62
  'data-testid': dataTestId,
62
63
  disabled,
64
+ 'aria-label': ariaLabel,
63
65
  },
64
66
  ref
65
67
  ) {
@@ -118,7 +120,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
118
120
  // `onChange` with a duplicated text value.
119
121
  if (isComposing.current) return
120
122
  e.currentTarget.blur()
121
- stopEventPropagation(e)
123
+ e.stopPropagation()
122
124
  onComplete?.(e.currentTarget.value)
123
125
  break
124
126
  }
@@ -126,7 +128,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
126
128
  e.currentTarget.value = rInitialValue.current
127
129
  onCancel?.(e.currentTarget.value)
128
130
  e.currentTarget.blur()
129
- stopEventPropagation(e)
131
+ e.stopPropagation()
130
132
  break
131
133
  }
132
134
  }
@@ -198,6 +200,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
198
200
  onCompositionStart={handleCompositionStart}
199
201
  onCompositionEnd={handleCompositionEnd}
200
202
  autoFocus={autoFocus}
203
+ aria-label={ariaLabel}
201
204
  placeholder={placeholder}
202
205
  value={value}
203
206
  data-testid={dataTestId}
@@ -71,6 +71,7 @@ export const TldrawUiToolbarButton = React.forwardRef<HTMLButtonElement, TLUiToo
71
71
  draggable={false}
72
72
  data-isactive={isActive}
73
73
  {...props}
74
+ aria-label={props.title}
74
75
  // The tooltip takes care of this.
75
76
  title={undefined}
76
77
  className={classnames('tlui-button', `tlui-button__${type}`, props.className)}
@@ -127,7 +128,7 @@ export interface TLUiToolbarToggleItemProps extends React.HTMLAttributes<HTMLBut
127
128
  className?: string
128
129
  type: 'icon' | 'tool'
129
130
  value: string
130
- tooltip?: string
131
+ tooltip?: React.ReactNode
131
132
  }
132
133
 
133
134
  /** @public @react */
@@ -171,6 +171,20 @@ function TooltipSingleton() {
171
171
  }
172
172
  }, [cameraState, isOpen, currentTooltip, editor])
173
173
 
174
+ useEffect(() => {
175
+ function handleKeyDown(event: KeyboardEvent) {
176
+ if (event.key === 'Escape' && currentTooltip && isOpen) {
177
+ tooltipManager.hideTooltip(editor, currentTooltip.id)
178
+ event.stopPropagation()
179
+ }
180
+ }
181
+
182
+ document.addEventListener('keydown', handleKeyDown, { capture: true })
183
+ return () => {
184
+ document.removeEventListener('keydown', handleKeyDown, { capture: true })
185
+ }
186
+ }, [editor, currentTooltip, isOpen])
187
+
174
188
  // Update open state and trigger position
175
189
  useEffect(() => {
176
190
  let timer: ReturnType<typeof setTimeout> | null = null
@@ -293,9 +307,9 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
293
307
  }
294
308
 
295
309
  // Fallback to old behavior if no provider
296
- if (!hasProvider) {
310
+ if (!hasProvider || showUiLabels) {
297
311
  return (
298
- <_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>
312
+ <_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent={!showUiLabels}>
299
313
  <_Tooltip.Trigger asChild ref={ref}>
300
314
  {children}
301
315
  </_Tooltip.Trigger>
@@ -19,6 +19,7 @@ export interface TLUiMenuCheckboxItemProps<
19
19
  kbd?: string
20
20
  title?: string
21
21
  label?: TranslationKey | { [key: string]: TranslationKey }
22
+ lang?: string
22
23
  readonlyOk?: boolean
23
24
  onSelect(source: TLUiEventSource): Promise<void> | void
24
25
  toggle?: boolean
@@ -34,6 +35,7 @@ export function TldrawUiMenuCheckboxItem<
34
35
  id,
35
36
  kbd,
36
37
  label,
38
+ lang,
37
39
  readonlyOk,
38
40
  onSelect,
39
41
  toggle = false,
@@ -55,6 +57,7 @@ export function TldrawUiMenuCheckboxItem<
55
57
  return (
56
58
  <_DropdownMenu.CheckboxItem
57
59
  dir="ltr"
60
+ lang={lang}
58
61
  className="tlui-button tlui-button__menu tlui-button__checkbox"
59
62
  title={labelStr}
60
63
  onSelect={(e) => {
@@ -84,6 +87,7 @@ export function TldrawUiMenuCheckboxItem<
84
87
  key={id}
85
88
  className="tlui-button tlui-button__menu tlui-button__checkbox"
86
89
  dir="ltr"
90
+ lang={lang}
87
91
  title={labelStr}
88
92
  onSelect={(e) => {
89
93
  onSelect(sourceId)
@@ -1584,6 +1584,19 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
1584
1584
  onSelect: async (source) => {
1585
1585
  if (!canApplySelectionAction()) return
1586
1586
 
1587
+ const onlySelectedShape = editor.getOnlySelectedShape()
1588
+ if (
1589
+ onlySelectedShape &&
1590
+ (editor.isShapeOfType<TLImageShape>(onlySelectedShape, 'image') ||
1591
+ editor.isShapeOfType<TLVideoShape>(onlySelectedShape, 'video'))
1592
+ ) {
1593
+ const firstToolbarButton = editor
1594
+ .getContainer()
1595
+ .querySelector('.tlui-contextual-toolbar button:first-child') as HTMLElement | null
1596
+ firstToolbarButton?.focus()
1597
+ return
1598
+ }
1599
+
1587
1600
  const firstButton = editor
1588
1601
  .getContainer()
1589
1602
  .querySelector('.tlui-style-panel button') as HTMLElement | null
@@ -7,8 +7,8 @@ import {
7
7
  assert,
8
8
  compact,
9
9
  isDefined,
10
+ markEventAsHandled,
10
11
  preventDefault,
11
- stopEventPropagation,
12
12
  uniq,
13
13
  useEditor,
14
14
  useMaybeEditor,
@@ -763,7 +763,7 @@ export function useNativeClipboardEvents() {
763
763
 
764
764
  const paste = (e: ClipboardEvent) => {
765
765
  if (disablingMiddleClickPaste) {
766
- stopEventPropagation(e)
766
+ markEventAsHandled(e)
767
767
  return
768
768
  }
769
769
 
@@ -122,6 +122,7 @@ export type TLUiTranslationKey =
122
122
  | 'action.zoom-to-fit'
123
123
  | 'action.zoom-to-selection'
124
124
  | 'assets.files.size-too-big'
125
+ | 'assets.files.maximum-size'
125
126
  | 'assets.files.type-not-allowed'
126
127
  | 'assets.files.upload-failed'
127
128
  | 'assets.files.amount-too-many'
@@ -411,6 +412,7 @@ export type TLUiTranslationKey =
411
412
  | 'style-panel.opacity'
412
413
  | 'style-panel.size'
413
414
  | 'style-panel.spline'
415
+ | 'style-panel.selected'
414
416
  | 'tool-panel.title'
415
417
  | 'tool-panel.more'
416
418
  | 'navigation-zone.title'
@@ -123,6 +123,7 @@ export const DEFAULT_TRANSLATION = {
123
123
  'action.zoom-to-fit': 'Zoom to fit',
124
124
  'action.zoom-to-selection': 'Zoom to selection',
125
125
  'assets.files.size-too-big': 'File size is too big',
126
+ 'assets.files.maximum-size': 'Maximum file size is {size}',
126
127
  'assets.files.type-not-allowed': 'File type is not allowed',
127
128
  'assets.files.upload-failed': 'Upload failed',
128
129
  'assets.files.amount-too-many': 'Too many files',
@@ -414,6 +415,7 @@ export const DEFAULT_TRANSLATION = {
414
415
  'style-panel.opacity': 'Opacity',
415
416
  'style-panel.size': 'Size',
416
417
  'style-panel.spline': 'Spline',
418
+ 'style-panel.selected': 'selected',
417
419
  'tool-panel.title': 'Tools',
418
420
  'tool-panel.more': 'More',
419
421
  'navigation-zone.title': 'Navigation',
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.16.0-canary.faec5de49906'
4
+ export const version = '3.16.0-canary.fe4babe9c1ad'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-09-11T13:12:32.426Z',
8
- patch: '2025-09-11T13:12:32.426Z',
7
+ minor: '2025-09-17T14:16:55.791Z',
8
+ patch: '2025-09-17T14:16:55.791Z',
9
9
  }
package/src/lib/ui.css CHANGED
@@ -494,6 +494,10 @@
494
494
  -webkit-user-select: auto !important;
495
495
  }
496
496
 
497
+ .tlui-input::placeholder {
498
+ color: var(--tl-color-text-3);
499
+ }
500
+
497
501
  .tlui-input__wrapper {
498
502
  width: 100%;
499
503
  height: 44px;
@@ -578,6 +582,12 @@
578
582
  box-shadow: var(--tl-shadow-3);
579
583
  }
580
584
 
585
+ @media (max-height: 600px) {
586
+ .tlui-menu {
587
+ max-height: 70vh;
588
+ }
589
+ }
590
+
581
591
  .tlui-menu::-webkit-scrollbar {
582
592
  display: none;
583
593
  }
package/tldraw.css CHANGED
@@ -2290,6 +2290,10 @@ it from receiving any pointer events or affecting the cursor. */
2290
2290
  -webkit-user-select: auto !important;
2291
2291
  }
2292
2292
 
2293
+ .tlui-input::placeholder {
2294
+ color: var(--tl-color-text-3);
2295
+ }
2296
+
2293
2297
  .tlui-input__wrapper {
2294
2298
  width: 100%;
2295
2299
  height: 44px;
@@ -2374,6 +2378,12 @@ it from receiving any pointer events or affecting the cursor. */
2374
2378
  box-shadow: var(--tl-shadow-3);
2375
2379
  }
2376
2380
 
2381
+ @media (max-height: 600px) {
2382
+ .tlui-menu {
2383
+ max-height: 70vh;
2384
+ }
2385
+ }
2386
+
2377
2387
  .tlui-menu::-webkit-scrollbar {
2378
2388
  display: none;
2379
2389
  }