@tldraw/editor 3.12.0-canary.72b63fdade8b → 3.12.0-canary.81c3581f0bf0

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 (54) hide show
  1. package/dist-cjs/index.d.ts +5 -2
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/TldrawEditor.js.map +1 -1
  4. package/dist-cjs/lib/components/Shape.js +10 -14
  5. package/dist-cjs/lib/components/Shape.js.map +2 -2
  6. package/dist-cjs/lib/editor/Editor.js +7 -12
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
  9. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/FontManager.js +1 -1
  11. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  12. package/dist-cjs/lib/editor/tools/StateNode.js +4 -1
  13. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  14. package/dist-cjs/lib/hooks/useCanvasEvents.js +12 -7
  15. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +3 -3
  16. package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
  17. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  18. package/dist-cjs/lib/utils/debug-flags.js +2 -1
  19. package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
  20. package/dist-cjs/version.js +3 -3
  21. package/dist-cjs/version.js.map +1 -1
  22. package/dist-esm/index.d.mts +5 -2
  23. package/dist-esm/index.mjs +1 -1
  24. package/dist-esm/lib/TldrawEditor.mjs.map +1 -1
  25. package/dist-esm/lib/components/Shape.mjs +11 -15
  26. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  27. package/dist-esm/lib/editor/Editor.mjs +7 -12
  28. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  29. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
  30. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  31. package/dist-esm/lib/editor/managers/FontManager.mjs +1 -1
  32. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  33. package/dist-esm/lib/editor/tools/StateNode.mjs +4 -1
  34. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  35. package/dist-esm/lib/hooks/useCanvasEvents.mjs +12 -7
  36. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +3 -3
  37. package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
  38. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  39. package/dist-esm/lib/utils/debug-flags.mjs +2 -1
  40. package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
  41. package/dist-esm/version.mjs +3 -3
  42. package/dist-esm/version.mjs.map +1 -1
  43. package/editor.css +5 -4
  44. package/package.json +7 -7
  45. package/src/lib/TldrawEditor.tsx +2 -2
  46. package/src/lib/components/Shape.tsx +15 -19
  47. package/src/lib/editor/Editor.ts +7 -13
  48. package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
  49. package/src/lib/editor/managers/FontManager.ts +1 -1
  50. package/src/lib/editor/tools/StateNode.ts +6 -1
  51. package/src/lib/hooks/useCanvasEvents.ts +14 -7
  52. package/src/lib/hooks/useGestureEvents.ts +12 -6
  53. package/src/lib/utils/debug-flags.ts +1 -0
  54. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  import { react } from '@tldraw/state'
2
2
  import { useQuickReactor, useStateTracking } from '@tldraw/state-react'
3
3
  import { TLShape, TLShapeId } from '@tldraw/tlschema'
4
- import { memo, useCallback, useEffect, useRef } from 'react'
4
+ import { memo, useCallback, useEffect, useLayoutEffect, useRef } from 'react'
5
5
  import { ShapeUtil } from '../editor/shapes/ShapeUtil'
6
6
  import { useEditor } from '../hooks/useEditor'
7
7
  import { useEditorComponents } from '../hooks/useEditorComponents'
@@ -44,10 +44,10 @@ export const Shape = memo(function Shape({
44
44
 
45
45
  useEffect(() => {
46
46
  return react('load fonts', () => {
47
- const fonts = editor.fonts.getShapeFontFaces(shape)
47
+ const fonts = editor.fonts.getShapeFontFaces(id)
48
48
  editor.fonts.requestFonts(fonts)
49
49
  })
50
- }, [editor, shape])
50
+ }, [editor, id])
51
51
 
52
52
  const memoizedStuffRef = useRef({
53
53
  transform: '',
@@ -104,22 +104,18 @@ export const Shape = memo(function Shape({
104
104
  )
105
105
 
106
106
  // This stuff changes pretty infrequently, so we can change them together
107
- useQuickReactor(
108
- 'set opacity and z-index',
109
- () => {
110
- const container = containerRef.current
111
- const bgContainer = bgContainerRef.current
112
-
113
- // Opacity
114
- setStyleProperty(container, 'opacity', opacity)
115
- setStyleProperty(bgContainer, 'opacity', opacity)
116
-
117
- // Z-Index
118
- setStyleProperty(container, 'z-index', index)
119
- setStyleProperty(bgContainer, 'z-index', backgroundIndex)
120
- },
121
- [opacity, index, backgroundIndex]
122
- )
107
+ useLayoutEffect(() => {
108
+ const container = containerRef.current
109
+ const bgContainer = bgContainerRef.current
110
+
111
+ // Opacity
112
+ setStyleProperty(container, 'opacity', opacity)
113
+ setStyleProperty(bgContainer, 'opacity', opacity)
114
+
115
+ // Z-Index
116
+ setStyleProperty(container, 'z-index', index)
117
+ setStyleProperty(bgContainer, 'z-index', backgroundIndex)
118
+ }, [opacity, index, backgroundIndex])
123
119
 
124
120
  useQuickReactor(
125
121
  'set display',
@@ -1216,7 +1216,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1216
1216
  run(fn: () => void, opts?: TLEditorRunOptions): this {
1217
1217
  const previousIgnoreShapeLock = this._shouldIgnoreShapeLock
1218
1218
  this._shouldIgnoreShapeLock = opts?.ignoreShapeLock ?? previousIgnoreShapeLock
1219
-
1220
1219
  try {
1221
1220
  this.history.batch(fn, opts)
1222
1221
  } finally {
@@ -9859,12 +9858,12 @@ export class Editor extends EventEmitter<TLEventMap> {
9859
9858
 
9860
9859
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
9861
9860
 
9862
- const { panSpeed, zoomSpeed } = cameraOptions
9861
+ const { panSpeed } = cameraOptions
9863
9862
  this._setCamera(
9864
9863
  new Vec(
9865
- cx + (dx * panSpeed) / cz - x / cz + x / (z * zoomSpeed),
9866
- cy + (dy * panSpeed) / cz - y / cz + y / (z * zoomSpeed),
9867
- z * zoomSpeed
9864
+ cx + (dx * panSpeed) / cz - x / cz + x / z,
9865
+ cy + (dy * panSpeed) / cz - y / cz + y / z,
9866
+ z
9868
9867
  ),
9869
9868
  { immediate: true }
9870
9869
  )
@@ -9939,14 +9938,9 @@ export class Editor extends EventEmitter<TLEventMap> {
9939
9938
  }
9940
9939
 
9941
9940
  const zoom = cz + (delta ?? 0) * zoomSpeed * cz
9942
- this._setCamera(
9943
- new Vec(
9944
- cx + (x / zoom - x) - (x / cz - x),
9945
- cy + (y / zoom - y) - (y / cz - y),
9946
- zoom
9947
- ),
9948
- { immediate: true }
9949
- )
9941
+ this._setCamera(new Vec(cx + x / zoom - x / cz, cy + y / zoom - y / cz, zoom), {
9942
+ immediate: true,
9943
+ })
9950
9944
  this.maybeTrackPerformance('Zooming')
9951
9945
  return
9952
9946
  }
@@ -31,7 +31,7 @@ export const notVisibleShapes = (editor: Editor) => {
31
31
  })
32
32
  return notVisibleShapes
33
33
  }
34
- return computed<Set<TLShapeId>>('getCulledShapes', (prevValue) => {
34
+ return computed<Set<TLShapeId>>('notVisibleShapes', (prevValue) => {
35
35
  if (isUninitialized(prevValue)) {
36
36
  return fromScratch(editor)
37
37
  }
@@ -94,7 +94,7 @@ export class FontManager {
94
94
  const shapeUtil = this.editor.getShapeUtil(shape)
95
95
  return shapeUtil.getFontFaces(shape)
96
96
  },
97
- { areResultsEqual: areArraysShallowEqual }
97
+ { areResultsEqual: areArraysShallowEqual, areRecordsEqual: (a, b) => a.props === b.props }
98
98
  )
99
99
 
100
100
  this.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(
@@ -38,6 +38,7 @@ export interface TLStateNodeConstructor {
38
38
  initial?: string
39
39
  children?(): TLStateNodeConstructor[]
40
40
  isLockable: boolean
41
+ useCoalescedEvents: boolean
41
42
  }
42
43
 
43
44
  /** @public */
@@ -47,7 +48,8 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
47
48
  public editor: Editor,
48
49
  parent?: StateNode
49
50
  ) {
50
- const { id, children, initial, isLockable } = this.constructor as TLStateNodeConstructor
51
+ const { id, children, initial, isLockable, useCoalescedEvents } = this
52
+ .constructor as TLStateNodeConstructor
51
53
 
52
54
  this.id = id
53
55
  this._isActive = atom<boolean>('toolIsActive' + this.id, false)
@@ -83,6 +85,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
83
85
  }
84
86
  }
85
87
  this.isLockable = isLockable
88
+ this.useCoalescedEvents = useCoalescedEvents
86
89
  this.performanceTracker = new PerformanceTracker()
87
90
  }
88
91
 
@@ -90,6 +93,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
90
93
  static initial?: string
91
94
  static children?: () => TLStateNodeConstructor[]
92
95
  static isLockable = true
96
+ static useCoalescedEvents = false
93
97
 
94
98
  id: string
95
99
  type: 'branch' | 'leaf' | 'root'
@@ -97,6 +101,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
97
101
  initial?: string
98
102
  children?: Record<string, StateNode>
99
103
  isLockable: boolean
104
+ useCoalescedEvents: boolean
100
105
  parent: StateNode
101
106
 
102
107
  /**
@@ -1,3 +1,4 @@
1
+ import { useValue } from '@tldraw/state-react'
1
2
  import React, { useMemo } from 'react'
2
3
  import { RIGHT_MOUSE_BUTTON } from '../constants'
3
4
  import {
@@ -11,6 +12,7 @@ import { useEditor } from './useEditor'
11
12
 
12
13
  export function useCanvasEvents() {
13
14
  const editor = useEditor()
15
+ const currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])
14
16
 
15
17
  const events = useMemo(
16
18
  function canvasEvents() {
@@ -49,12 +51,17 @@ export function useCanvasEvents() {
49
51
  lastX = e.clientX
50
52
  lastY = e.clientY
51
53
 
52
- editor.dispatch({
53
- type: 'pointer',
54
- target: 'canvas',
55
- name: 'pointer_move',
56
- ...getPointerInfo(e),
57
- })
54
+ // For tools that benefit from a higher fidelity of events,
55
+ // we dispatch the coalesced events.
56
+ const events = currentTool.useCoalescedEvents ? e.nativeEvent.getCoalescedEvents() : [e]
57
+ for (const singleEvent of events) {
58
+ editor.dispatch({
59
+ type: 'pointer',
60
+ target: 'canvas',
61
+ name: 'pointer_move',
62
+ ...getPointerInfo(singleEvent),
63
+ })
64
+ }
58
65
  }
59
66
 
60
67
  function onPointerUp(e: React.PointerEvent) {
@@ -159,7 +166,7 @@ export function useCanvasEvents() {
159
166
  onClick,
160
167
  }
161
168
  },
162
- [editor]
169
+ [editor, currentTool]
163
170
  )
164
171
 
165
172
  return events
@@ -135,7 +135,6 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
135
135
 
136
136
  let initDistanceBetweenFingers = 1 // the distance between the two fingers when the pinch starts
137
137
  let initZoom = 1 // the browser's zoom level when the pinch starts
138
- let currZoom = 1 // the current zoom level according to the pinch gesture recognizer
139
138
  let currDistanceBetweenFingers = 0
140
139
  const initPointBetweenFingers = new Vec()
141
140
  const prevPointBetweenFingers = new Vec()
@@ -239,7 +238,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
239
238
 
240
239
  switch (pinchState) {
241
240
  case 'zooming': {
242
- currZoom = offset[0]
241
+ const currZoom = offset[0] ** editor.getCameraOptions().zoomSpeed
243
242
 
244
243
  editor.dispatch({
245
244
  type: 'pinch',
@@ -278,7 +277,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
278
277
  if (event instanceof WheelEvent) return
279
278
  if (!(event.target === elm || elm?.contains(event.target as Node))) return
280
279
 
281
- const scale = offset[0]
280
+ const scale = offset[0] ** editor.getCameraOptions().zoomSpeed
282
281
 
283
282
  pinchState = 'not sure'
284
283
 
@@ -309,14 +308,21 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
309
308
  target: ref,
310
309
  eventOptions: { passive: false },
311
310
  pinch: {
312
- from: () => [editor.getZoomLevel(), 0], // Return the camera z to use when pinch starts
311
+ from: () => {
312
+ const { zoomSpeed } = editor.getCameraOptions()
313
+ const level = editor.getZoomLevel() ** (1 / zoomSpeed)
314
+ return [level, 0]
315
+ }, // Return the camera z to use when pinch starts
313
316
  scaleBounds: () => {
314
317
  const baseZoom = editor.getBaseZoom()
315
- const zoomSteps = editor.getCameraOptions().zoomSteps
318
+ const { zoomSteps, zoomSpeed } = editor.getCameraOptions()
316
319
  const zoomMin = zoomSteps[0] * baseZoom
317
320
  const zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom
318
321
 
319
- return { from: editor.getZoomLevel(), max: zoomMax, min: zoomMin }
322
+ return {
323
+ max: zoomMax ** (1 / zoomSpeed),
324
+ min: zoomMin ** (1 / zoomSpeed),
325
+ }
320
326
  },
321
327
  },
322
328
  })
@@ -53,6 +53,7 @@ export const debugFlags = {
53
53
  debugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),
54
54
  hideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),
55
55
  editOnType: createDebugValue('editOnType', { defaults: { all: false } }),
56
+ a11y: createDebugValue('a11y', { defaults: { all: false } }),
56
57
  } as const
57
58
 
58
59
  declare global {
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.12.0-canary.72b63fdade8b'
4
+ export const version = '3.12.0-canary.81c3581f0bf0'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-03-24T12:19:50.041Z',
8
- patch: '2025-03-24T12:19:50.041Z',
7
+ minor: '2025-04-02T14:26:41.813Z',
8
+ patch: '2025-04-02T14:26:41.813Z',
9
9
  }