@tldraw/editor 3.16.0-canary.fa3749606e52 → 3.16.0-canary.ffdf566dd0a8

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 (57) hide show
  1. package/dist-cjs/index.d.ts +80 -9
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +1 -1
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +5 -0
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/config/TLUserPreferences.js +8 -2
  9. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  10. package/dist-cjs/lib/editor/Editor.js +19 -9
  11. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  12. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -3
  13. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  14. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  15. package/dist-cjs/lib/hooks/useCanvasEvents.js +20 -24
  16. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  17. package/dist-cjs/lib/options.js +1 -0
  18. package/dist-cjs/lib/options.js.map +2 -2
  19. package/dist-cjs/lib/utils/EditorAtom.js +45 -0
  20. package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
  21. package/dist-cjs/version.js +3 -3
  22. package/dist-cjs/version.js.map +1 -1
  23. package/dist-esm/index.d.mts +80 -9
  24. package/dist-esm/index.mjs +3 -1
  25. package/dist-esm/index.mjs.map +2 -2
  26. package/dist-esm/lib/TldrawEditor.mjs +1 -1
  27. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  28. package/dist-esm/lib/components/MenuClickCapture.mjs +5 -0
  29. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  30. package/dist-esm/lib/config/TLUserPreferences.mjs +8 -2
  31. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  32. package/dist-esm/lib/editor/Editor.mjs +19 -9
  33. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  34. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -3
  35. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  36. package/dist-esm/lib/hooks/useCanvasEvents.mjs +21 -25
  37. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  38. package/dist-esm/lib/options.mjs +1 -0
  39. package/dist-esm/lib/options.mjs.map +2 -2
  40. package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
  41. package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
  42. package/dist-esm/version.mjs +3 -3
  43. package/dist-esm/version.mjs.map +1 -1
  44. package/editor.css +2 -0
  45. package/package.json +7 -7
  46. package/src/index.ts +2 -0
  47. package/src/lib/TldrawEditor.tsx +5 -5
  48. package/src/lib/components/MenuClickCapture.tsx +8 -0
  49. package/src/lib/config/TLUserPreferences.ts +7 -0
  50. package/src/lib/editor/Editor.ts +33 -27
  51. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +13 -0
  52. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -0
  53. package/src/lib/editor/types/misc-types.ts +54 -1
  54. package/src/lib/hooks/useCanvasEvents.ts +32 -39
  55. package/src/lib/options.ts +2 -0
  56. package/src/lib/utils/EditorAtom.ts +37 -0
  57. package/src/version.ts +3 -3
@@ -25,6 +25,7 @@ describe('UserPreferencesManager', () => {
25
25
  locale: 'en',
26
26
  animationSpeed: 1,
27
27
  areKeyboardShortcutsEnabled: true,
28
+ showUiLabels: false,
28
29
  edgeScrollSpeed: 1,
29
30
  colorScheme: 'light',
30
31
  isSnapMode: false,
@@ -231,6 +232,7 @@ describe('UserPreferencesManager', () => {
231
232
  color: mockUserPreferences.color,
232
233
  animationSpeed: mockUserPreferences.animationSpeed,
233
234
  areKeyboardShortcutsEnabled: mockUserPreferences.areKeyboardShortcutsEnabled,
235
+ showUiLabels: mockUserPreferences.showUiLabels,
234
236
  isSnapMode: mockUserPreferences.isSnapMode,
235
237
  colorScheme: mockUserPreferences.colorScheme,
236
238
  isDarkMode: false, // light mode
@@ -379,6 +381,17 @@ describe('UserPreferencesManager', () => {
379
381
  })
380
382
  })
381
383
 
384
+ describe('getShowUiLabels', () => {
385
+ it('should return user show ui labels setting', () => {
386
+ expect(userPreferencesManager.getShowUiLabels()).toBe(mockUserPreferences.showUiLabels)
387
+ })
388
+
389
+ it('should return default show ui labels when null', () => {
390
+ userPreferencesAtom.set({ ...mockUserPreferences, showUiLabels: null })
391
+ expect(userPreferencesManager.getShowUiLabels()).toBe(defaultUserPreferences.showUiLabels)
392
+ })
393
+ })
394
+
382
395
  describe('getEdgeScrollSpeed', () => {
383
396
  it('should return user edge scroll speed', () => {
384
397
  expect(userPreferencesManager.getEdgeScrollSpeed()).toBe(
@@ -49,6 +49,7 @@ export class UserPreferencesManager {
49
49
  isDarkMode: this.getIsDarkMode(),
50
50
  isWrapMode: this.getIsWrapMode(),
51
51
  isDynamicResizeMode: this.getIsDynamicResizeMode(),
52
+ showUiLabels: this.getShowUiLabels(),
52
53
  }
53
54
  }
54
55
 
@@ -119,4 +120,8 @@ export class UserPreferencesManager {
119
120
  defaultUserPreferences.isPasteAtCursorMode
120
121
  )
121
122
  }
123
+
124
+ @computed getShowUiLabels() {
125
+ return this.user.userPreferences.get().showUiLabels ?? defaultUserPreferences.showUiLabels
126
+ }
122
127
  }
@@ -1,4 +1,4 @@
1
- import { BoxModel } from '@tldraw/tlschema'
1
+ import { BoxModel, TLShape } from '@tldraw/tlschema'
2
2
  import { Box } from '../../primitives/Box'
3
3
  import { VecLike } from '../../primitives/Vec'
4
4
 
@@ -206,3 +206,56 @@ export interface TLUpdatePointerOptions {
206
206
  isPen?: boolean
207
207
  button?: number
208
208
  }
209
+
210
+ /**
211
+ * Options to {@link Editor.getShapeAtPoint}.
212
+ *
213
+ * @public
214
+ */
215
+ export interface TLGetShapeAtPointOptions {
216
+ /**
217
+ * The margin to apply to the shape.
218
+ * If a number, it will be applied to both the inside and outside of the shape.
219
+ * If an array, the first element will be applied to the inside of the shape, and the second element will be applied to the outside.
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * // Get the shape at the center of the screen
224
+ * const shape = editor.getShapeAtProps({
225
+ * margin: 10,
226
+ * })
227
+ *
228
+ * // Get the shape at the center of the screen with a 10px inner margin and a 5px outer margin
229
+ * const shape = editor.getShapeAtProps({
230
+ * margin: [10, 5],
231
+ * })
232
+ * ```
233
+ */
234
+ margin?: number | [number, number]
235
+ /**
236
+ * Whether to register hits inside of shapes (beyond the margin), such as the inside of a solid shape.
237
+ */
238
+ hitInside?: boolean
239
+ /**
240
+ * Whether to register hits on locked shapes.
241
+ */
242
+ hitLocked?: boolean
243
+ /**
244
+ * Whether to register hits on labels.
245
+ */
246
+ hitLabels?: boolean
247
+ /**
248
+ * Whether to only return hits on shapes that are currently being rendered.
249
+ * todo: rename this to hitCulled or hitNotRendering
250
+ */
251
+ renderingOnly?: boolean
252
+ /**
253
+ * Whether to register hits on the inside of frame shapes.
254
+ * todo: rename this to hitInsideFrames
255
+ */
256
+ hitFrameInside?: boolean
257
+ /**
258
+ * A filter function to apply to the shapes.
259
+ */
260
+ filter?(shape: TLShape): boolean
261
+ }
@@ -1,5 +1,5 @@
1
1
  import { useValue } from '@tldraw/state-react'
2
- import React, { useEffect, useMemo } from 'react'
2
+ import React, { useMemo } from 'react'
3
3
  import { RIGHT_MOUSE_BUTTON } from '../constants'
4
4
  import {
5
5
  preventDefault,
@@ -16,6 +16,9 @@ export function useCanvasEvents() {
16
16
 
17
17
  const events = useMemo(
18
18
  function canvasEvents() {
19
+ // Track the last screen point
20
+ let lastX: number, lastY: number
21
+
19
22
  function onPointerDown(e: React.PointerEvent) {
20
23
  if ((e as any).isKilled) return
21
24
 
@@ -41,9 +44,35 @@ export function useCanvasEvents() {
41
44
  })
42
45
  }
43
46
 
47
+ function onPointerMove(e: React.PointerEvent) {
48
+ if ((e as any).isKilled) return
49
+
50
+ if (e.clientX === lastX && e.clientY === lastY) return
51
+ lastX = e.clientX
52
+ lastY = e.clientY
53
+
54
+ // For tools that benefit from a higher fidelity of events,
55
+ // we dispatch the coalesced events.
56
+ // N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.
57
+ const events =
58
+ currentTool.useCoalescedEvents && e.nativeEvent.getCoalescedEvents
59
+ ? e.nativeEvent.getCoalescedEvents()
60
+ : [e]
61
+ for (const singleEvent of events) {
62
+ editor.dispatch({
63
+ type: 'pointer',
64
+ target: 'canvas',
65
+ name: 'pointer_move',
66
+ ...getPointerInfo(singleEvent),
67
+ })
68
+ }
69
+ }
70
+
44
71
  function onPointerUp(e: React.PointerEvent) {
45
72
  if ((e as any).isKilled) return
46
73
  if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
74
+ lastX = e.clientX
75
+ lastY = e.clientY
47
76
 
48
77
  releasePointerCapture(e.currentTarget, e)
49
78
 
@@ -129,6 +158,7 @@ export function useCanvasEvents() {
129
158
 
130
159
  return {
131
160
  onPointerDown,
161
+ onPointerMove,
132
162
  onPointerUp,
133
163
  onPointerEnter,
134
164
  onPointerLeave,
@@ -139,45 +169,8 @@ export function useCanvasEvents() {
139
169
  onClick,
140
170
  }
141
171
  },
142
- [editor]
172
+ [editor, currentTool]
143
173
  )
144
174
 
145
- // onPointerMove is special: where we're only interested in the other events when they're
146
- // happening _on_ the canvas (as opposed to outside of it, or on UI floating over it), we want
147
- // the pointer position to be up to date regardless of whether it's over the tldraw canvas or
148
- // not. So instead of returning a listener to be attached to the canvas, we directly attach a
149
- // listener to the whole document instead.
150
- useEffect(() => {
151
- let lastX: number, lastY: number
152
-
153
- function onPointerMove(e: PointerEvent) {
154
- if ((e as any).isKilled) return
155
- ;(e as any).isKilled = true
156
-
157
- if (e.clientX === lastX && e.clientY === lastY) return
158
- lastX = e.clientX
159
- lastY = e.clientY
160
-
161
- // For tools that benefit from a higher fidelity of events,
162
- // we dispatch the coalesced events.
163
- // N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.
164
- const events =
165
- currentTool.useCoalescedEvents && e.getCoalescedEvents ? e.getCoalescedEvents() : [e]
166
- for (const singleEvent of events) {
167
- editor.dispatch({
168
- type: 'pointer',
169
- target: 'canvas',
170
- name: 'pointer_move',
171
- ...getPointerInfo(singleEvent),
172
- })
173
- }
174
- }
175
-
176
- document.body.addEventListener('pointermove', onPointerMove)
177
- return () => {
178
- document.body.removeEventListener('pointermove', onPointerMove)
179
- }
180
- }, [editor, currentTool])
181
-
182
175
  return events
183
176
  }
@@ -53,6 +53,7 @@ export interface TldrawOptions {
53
53
  readonly flattenImageBoundsPadding: number
54
54
  readonly laserDelayMs: number
55
55
  readonly maxExportDelayMs: number
56
+ readonly tooltipDelayMs: number
56
57
  /**
57
58
  * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before
58
59
  * they expire? Defaults to 3 minutes.
@@ -124,6 +125,7 @@ export const defaultTldrawOptions = {
124
125
  flattenImageBoundsPadding: 16,
125
126
  laserDelayMs: 1200,
126
127
  maxExportDelayMs: 5000,
128
+ tooltipDelayMs: 700,
127
129
  temporaryAssetPreviewLifetimeMs: 180000,
128
130
  actionShortcutsLocation: 'swap',
129
131
  createTextOnCanvasDoubleClick: true,
@@ -0,0 +1,37 @@
1
+ import { atom, Atom } from '@tldraw/state'
2
+ import { WeakCache } from '@tldraw/utils'
3
+ import { Editor } from '../editor/Editor'
4
+
5
+ /**
6
+ * An Atom that is scoped to the lifetime of an Editor.
7
+ *
8
+ * This is useful for storing UI state for tldraw applications. Keeping state scoped to an editor
9
+ * instead of stored in a global atom can prevent issues with state being shared between editors
10
+ * when navigating between pages, or when multiple editor instances are used on the same page.
11
+ *
12
+ * @public
13
+ */
14
+ export class EditorAtom<T> {
15
+ private states = new WeakCache<Editor, Atom<T>>()
16
+
17
+ constructor(
18
+ private name: string,
19
+ private getInitialState: (editor: Editor) => T
20
+ ) {}
21
+
22
+ getAtom(editor: Editor): Atom<T> {
23
+ return this.states.get(editor, () => atom(this.name, this.getInitialState(editor)))
24
+ }
25
+
26
+ get(editor: Editor): T {
27
+ return this.getAtom(editor).get()
28
+ }
29
+
30
+ update(editor: Editor, update: (state: T) => T): T {
31
+ return this.getAtom(editor).update(update)
32
+ }
33
+
34
+ set(editor: Editor, state: T): T {
35
+ return this.getAtom(editor).set(state)
36
+ }
37
+ }
package/src/version.ts CHANGED
@@ -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.fa3749606e52'
4
+ export const version = '3.16.0-canary.ffdf566dd0a8'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-08-01T14:30:58.099Z',
8
- patch: '2025-08-01T14:30:58.099Z',
7
+ minor: '2025-08-06T13:55:07.190Z',
8
+ patch: '2025-08-06T13:55:07.190Z',
9
9
  }