@tldraw/editor 5.3.2 → 5.4.0-canary.0063438d1c75

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 (55) hide show
  1. package/dist-cjs/index.d.ts +94 -52
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +86 -26
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +7 -3
  7. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
  8. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +6 -3
  9. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
  11. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  12. package/dist-cjs/lib/globals/editors.js +57 -0
  13. package/dist-cjs/lib/globals/editors.js.map +7 -0
  14. package/dist-cjs/lib/hooks/useViewportHeight.js.map +2 -2
  15. package/dist-cjs/lib/primitives/geometry/Group2d.js +63 -21
  16. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  17. package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
  18. package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
  19. package/dist-cjs/version.js +3 -3
  20. package/dist-cjs/version.js.map +1 -1
  21. package/dist-esm/index.d.mts +94 -52
  22. package/dist-esm/index.mjs +3 -1
  23. package/dist-esm/index.mjs.map +2 -2
  24. package/dist-esm/lib/editor/Editor.mjs +86 -26
  25. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  26. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
  27. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  28. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -4
  29. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  30. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  31. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  32. package/dist-esm/lib/globals/editors.mjs +37 -0
  33. package/dist-esm/lib/globals/editors.mjs.map +7 -0
  34. package/dist-esm/lib/hooks/useViewportHeight.mjs.map +2 -2
  35. package/dist-esm/lib/primitives/geometry/Group2d.mjs +63 -21
  36. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  37. package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
  38. package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
  39. package/dist-esm/version.mjs +3 -3
  40. package/dist-esm/version.mjs.map +1 -1
  41. package/package.json +7 -7
  42. package/src/index.ts +2 -0
  43. package/src/lib/editor/Editor.ts +126 -37
  44. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
  45. package/src/lib/editor/managers/TextManager/TextManager.test.ts +42 -0
  46. package/src/lib/editor/managers/TextManager/TextManager.ts +10 -8
  47. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  48. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  49. package/src/lib/globals/editors.test.ts +76 -0
  50. package/src/lib/globals/editors.ts +53 -0
  51. package/src/lib/hooks/useViewportHeight.ts +9 -1
  52. package/src/lib/primitives/geometry/Group2d.ts +63 -27
  53. package/src/lib/utils/normalizeWheel.test.ts +39 -0
  54. package/src/lib/utils/normalizeWheel.ts +6 -0
  55. package/src/version.ts +3 -3
@@ -1,4 +1,3 @@
1
- import { EMPTY_ARRAY } from '@tldraw/state'
2
1
  import { assert, invLerp, lerp } from '@tldraw/utils'
3
2
  import { Box } from '../Box'
4
3
  import { Mat } from '../Mat'
@@ -36,9 +35,15 @@ export class Group2d extends Geometry2d {
36
35
 
37
36
  override getVertices(filters: Geometry2dFilters): Vec[] {
38
37
  if (this.isExcludedByFilter(filters)) return []
39
- return this.children
40
- .filter((c) => !c.isExcludedByFilter(filters))
41
- .flatMap((c) => c.getVertices(filters))
38
+ const vertices: Vec[] = []
39
+ for (const child of this.children) {
40
+ if (child.isExcludedByFilter(filters)) continue
41
+ const childVertices = child.getVertices(filters)
42
+ for (let i = 0, n = childVertices.length; i < n; i++) {
43
+ vertices.push(childVertices[i])
44
+ }
45
+ }
46
+ return vertices
42
47
  }
43
48
 
44
49
  override nearestPoint(point: VecLike, filters?: Geometry2dFilters): Vec {
@@ -84,9 +89,11 @@ export class Group2d extends Geometry2d {
84
89
  hitInside: boolean,
85
90
  filters = Geometry2dFilters.EXCLUDE_LABELS
86
91
  ): boolean {
87
- return !!this.children
88
- .filter((c) => !c.isExcludedByFilter(filters))
89
- .find((c) => c.hitTestPoint(point, margin, hitInside))
92
+ for (const child of this.children) {
93
+ if (child.isExcludedByFilter(filters)) continue
94
+ if (child.hitTestPoint(point, margin, hitInside)) return true
95
+ }
96
+ return false
90
97
  }
91
98
 
92
99
  override hitTestLineSegment(
@@ -95,42 +102,71 @@ export class Group2d extends Geometry2d {
95
102
  zoom: number,
96
103
  filters = Geometry2dFilters.EXCLUDE_LABELS
97
104
  ): boolean {
98
- return !!this.children
99
- .filter((c) => !c.isExcludedByFilter(filters))
100
- .find((c) => c.hitTestLineSegment(A, B, zoom))
105
+ for (const child of this.children) {
106
+ if (child.isExcludedByFilter(filters)) continue
107
+ if (child.hitTestLineSegment(A, B, zoom)) return true
108
+ }
109
+ return false
101
110
  }
102
111
 
103
112
  override intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
104
- return this.children.flatMap((child) => {
105
- if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
106
- return child.intersectLineSegment(A, B, filters)
107
- })
113
+ const result: VecLike[] = []
114
+ for (const child of this.children) {
115
+ if (child.isExcludedByFilter(filters)) continue
116
+ const hits = child.intersectLineSegment(A, B, filters)
117
+ for (let i = 0, n = hits.length; i < n; i++) {
118
+ result.push(hits[i])
119
+ }
120
+ }
121
+ return result
108
122
  }
109
123
 
110
124
  override intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {
111
- return this.children.flatMap((child) => {
112
- if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
113
- return child.intersectCircle(center, radius, filters)
114
- })
125
+ const result: VecLike[] = []
126
+ for (const child of this.children) {
127
+ if (child.isExcludedByFilter(filters)) continue
128
+ const hits = child.intersectCircle(center, radius, filters)
129
+ for (let i = 0, n = hits.length; i < n; i++) {
130
+ result.push(hits[i])
131
+ }
132
+ }
133
+ return result
115
134
  }
116
135
 
117
136
  override getBoundsVertices(): Vec[] {
118
137
  if (this.excludeFromShapeBounds) return []
119
- return this.children.flatMap((child) => child.getBoundsVertices())
138
+ const vertices: Vec[] = []
139
+ for (const child of this.children) {
140
+ const childVertices = child.getBoundsVertices()
141
+ for (let i = 0, n = childVertices.length; i < n; i++) {
142
+ vertices.push(childVertices[i])
143
+ }
144
+ }
145
+ return vertices
120
146
  }
121
147
 
122
148
  override intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters) {
123
- return this.children.flatMap((child) => {
124
- if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
125
- return child.intersectPolygon(polygon, filters)
126
- })
149
+ const result: VecLike[] = []
150
+ for (const child of this.children) {
151
+ if (child.isExcludedByFilter(filters)) continue
152
+ const hits = child.intersectPolygon(polygon, filters)
153
+ for (let i = 0, n = hits.length; i < n; i++) {
154
+ result.push(hits[i])
155
+ }
156
+ }
157
+ return result
127
158
  }
128
159
 
129
160
  override intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters) {
130
- return this.children.flatMap((child) => {
131
- if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
132
- return child.intersectPolyline(polyline, filters)
133
- })
161
+ const result: VecLike[] = []
162
+ for (const child of this.children) {
163
+ if (child.isExcludedByFilter(filters)) continue
164
+ const hits = child.intersectPolyline(polyline, filters)
165
+ for (let i = 0, n = hits.length; i < n; i++) {
166
+ result.push(hits[i])
167
+ }
168
+ }
169
+ return result
134
170
  }
135
171
 
136
172
  override interpolateAlongEdge(t: number, filters?: Geometry2dFilters): Vec {
@@ -0,0 +1,39 @@
1
+ import { normalizeWheel } from './normalizeWheel'
2
+
3
+ function wheelEvent(props: Partial<WheelEvent>): WheelEvent {
4
+ return {
5
+ deltaX: 0,
6
+ deltaY: 0,
7
+ ctrlKey: false,
8
+ altKey: false,
9
+ metaKey: false,
10
+ shiftKey: false,
11
+ ...props,
12
+ } as WheelEvent
13
+ }
14
+
15
+ describe('normalizeWheel', () => {
16
+ it('negates finite deltas for panning', () => {
17
+ const { x, y } = normalizeWheel(wheelEvent({ deltaX: 5, deltaY: 10 }))
18
+ expect(x).toBe(-5)
19
+ expect(y).toBe(-10)
20
+ })
21
+
22
+ it('coerces a non-finite deltaX to zero movement', () => {
23
+ const { x, y } = normalizeWheel(wheelEvent({ deltaX: NaN, deltaY: 10 }))
24
+ expect(x === 0).toBe(true)
25
+ expect(y).toBe(-10)
26
+ })
27
+
28
+ it('coerces a non-finite deltaY to zero movement', () => {
29
+ const { x, y } = normalizeWheel(wheelEvent({ deltaX: 5, deltaY: Infinity }))
30
+ expect(x).toBe(-5)
31
+ expect(y === 0).toBe(true)
32
+ })
33
+
34
+ it('does not produce a non-finite zoom delta when wheeling with a bad deltaY', () => {
35
+ const { z } = normalizeWheel(wheelEvent({ deltaY: NaN, ctrlKey: true }))
36
+ expect(Number.isFinite(z)).toBe(true)
37
+ expect(z === 0).toBe(true)
38
+ })
39
+ })
@@ -9,6 +9,12 @@ const MAX_ZOOM_STEP = 10
9
9
  * @internal */
10
10
  export function normalizeWheel(event: WheelEvent | React.WheelEvent<HTMLElement>) {
11
11
  let { deltaY, deltaX } = event
12
+
13
+ // Coerce non-finite deltas to no movement so they can't propagate into the camera and
14
+ // crash validation. Also covers deltaZ below, which is derived from deltaY.
15
+ if (!Number.isFinite(deltaX)) deltaX = 0
16
+ if (!Number.isFinite(deltaY)) deltaY = 0
17
+
12
18
  let deltaZ = 0
13
19
 
14
20
  // wheeling
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 = '5.3.2'
4
+ export const version = '5.4.0-canary.0063438d1c75'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
- minor: '2026-08-05T11:39:54.978Z',
8
- patch: '2026-08-18T09:59:40.172Z',
7
+ minor: '2026-08-19T17:17:06.569Z',
8
+ patch: '2026-08-19T17:17:06.569Z',
9
9
  }