@tldraw/editor 3.16.0-canary.2e83e38fb91b → 3.16.0-canary.344cec0354f3

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 (95) hide show
  1. package/dist-cjs/index.d.ts +35 -32
  2. package/dist-cjs/index.js +2 -4
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +0 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -4
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  8. package/dist-cjs/lib/config/TLUserPreferences.js +15 -4
  9. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  10. package/dist-cjs/lib/editor/Editor.js +35 -1
  11. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  12. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
  13. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  14. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +11 -6
  15. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  16. package/dist-cjs/lib/hooks/useCanvasEvents.js +17 -17
  17. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  18. package/dist-cjs/lib/hooks/useDocumentEvents.js +4 -4
  19. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  20. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -1
  21. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  22. package/dist-cjs/lib/hooks/useHandleEvents.js +6 -6
  23. package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
  24. package/dist-cjs/lib/hooks/useSelectionEvents.js +8 -8
  25. package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
  26. package/dist-cjs/lib/license/LicenseManager.js +3 -0
  27. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  28. package/dist-cjs/lib/license/Watermark.js +97 -90
  29. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  30. package/dist-cjs/lib/utils/dom.js +1 -12
  31. package/dist-cjs/lib/utils/dom.js.map +2 -2
  32. package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
  33. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  34. package/dist-cjs/lib/utils/reparenting.js +5 -1
  35. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  36. package/dist-cjs/version.js +3 -3
  37. package/dist-cjs/version.js.map +1 -1
  38. package/dist-esm/index.d.mts +35 -32
  39. package/dist-esm/index.mjs +3 -7
  40. package/dist-esm/index.mjs.map +2 -2
  41. package/dist-esm/lib/TldrawEditor.mjs +0 -2
  42. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  43. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -5
  44. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  45. package/dist-esm/lib/config/TLUserPreferences.mjs +15 -4
  46. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  47. package/dist-esm/lib/editor/Editor.mjs +35 -1
  48. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  49. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
  50. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  51. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +11 -6
  52. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  53. package/dist-esm/lib/hooks/useCanvasEvents.mjs +18 -24
  54. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  55. package/dist-esm/lib/hooks/useDocumentEvents.mjs +5 -10
  56. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  57. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -2
  58. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  59. package/dist-esm/lib/hooks/useHandleEvents.mjs +7 -12
  60. package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
  61. package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -15
  62. package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
  63. package/dist-esm/lib/license/LicenseManager.mjs +3 -0
  64. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  65. package/dist-esm/lib/license/Watermark.mjs +98 -91
  66. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  67. package/dist-esm/lib/utils/dom.mjs +1 -12
  68. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  69. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
  70. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  71. package/dist-esm/lib/utils/reparenting.mjs +5 -1
  72. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  73. package/dist-esm/version.mjs +3 -3
  74. package/dist-esm/version.mjs.map +1 -1
  75. package/package.json +7 -7
  76. package/src/index.ts +0 -2
  77. package/src/lib/TldrawEditor.tsx +0 -2
  78. package/src/lib/components/default-components/DefaultCanvas.tsx +5 -5
  79. package/src/lib/config/TLUserPreferences.ts +16 -3
  80. package/src/lib/editor/Editor.ts +41 -1
  81. package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
  82. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +30 -8
  83. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +10 -3
  84. package/src/lib/hooks/useCanvasEvents.ts +18 -24
  85. package/src/lib/hooks/useDocumentEvents.ts +5 -10
  86. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
  87. package/src/lib/hooks/useHandleEvents.ts +7 -12
  88. package/src/lib/hooks/useSelectionEvents.ts +9 -15
  89. package/src/lib/license/LicenseManager.ts +3 -0
  90. package/src/lib/license/Watermark.tsx +100 -92
  91. package/src/lib/utils/dom.test.ts +33 -24
  92. package/src/lib/utils/dom.ts +1 -31
  93. package/src/lib/utils/getPointerInfo.ts +3 -3
  94. package/src/lib/utils/reparenting.ts +7 -1
  95. package/src/version.ts +3 -3
@@ -1,12 +1,7 @@
1
1
  import { TLArrowShape, TLLineShape, TLShapeId } from '@tldraw/tlschema'
2
2
  import * as React from 'react'
3
3
  import { Editor } from '../editor/Editor'
4
- import {
5
- loopToHtmlElement,
6
- releasePointerCapture,
7
- setPointerCapture,
8
- wasEventAlreadyHandled,
9
- } from '../utils/dom'
4
+ import { loopToHtmlElement, releasePointerCapture, setPointerCapture } from '../utils/dom'
10
5
  import { getPointerInfo } from '../utils/getPointerInfo'
11
6
  import { useEditor } from './useEditor'
12
7
 
@@ -21,7 +16,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
21
16
 
22
17
  return React.useMemo(() => {
23
18
  const onPointerDown = (e: React.PointerEvent) => {
24
- if (wasEventAlreadyHandled(e)) return
19
+ if (editor.wasEventAlreadyHandled(e)) return
25
20
 
26
21
  // Must set pointer capture on an HTML element!
27
22
  const target = loopToHtmlElement(e.currentTarget)
@@ -37,7 +32,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
37
32
  handle,
38
33
  shape,
39
34
  name: 'pointer_down',
40
- ...getPointerInfo(e),
35
+ ...getPointerInfo(editor, e),
41
36
  })
42
37
  }
43
38
 
@@ -45,7 +40,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
45
40
  let lastX: number, lastY: number
46
41
 
47
42
  const onPointerMove = (e: React.PointerEvent) => {
48
- if (wasEventAlreadyHandled(e)) return
43
+ if (editor.wasEventAlreadyHandled(e)) return
49
44
  if (e.clientX === lastX && e.clientY === lastY) return
50
45
  lastX = e.clientX
51
46
  lastY = e.clientY
@@ -60,12 +55,12 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
60
55
  handle,
61
56
  shape,
62
57
  name: 'pointer_move',
63
- ...getPointerInfo(e),
58
+ ...getPointerInfo(editor, e),
64
59
  })
65
60
  }
66
61
 
67
62
  const onPointerUp = (e: React.PointerEvent) => {
68
- if (wasEventAlreadyHandled(e)) return
63
+ if (editor.wasEventAlreadyHandled(e)) return
69
64
 
70
65
  const target = loopToHtmlElement(e.currentTarget)
71
66
  releasePointerCapture(target, e)
@@ -80,7 +75,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
80
75
  handle,
81
76
  shape,
82
77
  name: 'pointer_up',
83
- ...getPointerInfo(e),
78
+ ...getPointerInfo(editor, e),
84
79
  })
85
80
  }
86
81
 
@@ -1,13 +1,7 @@
1
1
  import { useMemo } from 'react'
2
2
  import { RIGHT_MOUSE_BUTTON } from '../constants'
3
3
  import { TLSelectionHandle } from '../editor/types/selection-types'
4
- import {
5
- loopToHtmlElement,
6
- markEventAsHandled,
7
- releasePointerCapture,
8
- setPointerCapture,
9
- wasEventAlreadyHandled,
10
- } from '../utils/dom'
4
+ import { loopToHtmlElement, releasePointerCapture, setPointerCapture } from '../utils/dom'
11
5
  import { getPointerInfo } from '../utils/getPointerInfo'
12
6
  import { useEditor } from './useEditor'
13
7
 
@@ -18,7 +12,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
18
12
  const events = useMemo(
19
13
  function selectionEvents() {
20
14
  const onPointerDown: React.PointerEventHandler = (e) => {
21
- if (wasEventAlreadyHandled(e)) return
15
+ if (editor.wasEventAlreadyHandled(e)) return
22
16
 
23
17
  if (e.button === RIGHT_MOUSE_BUTTON) {
24
18
  editor.dispatch({
@@ -26,7 +20,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
26
20
  target: 'selection',
27
21
  handle,
28
22
  name: 'right_click',
29
- ...getPointerInfo(e),
23
+ ...getPointerInfo(editor, e),
30
24
  })
31
25
  return
32
26
  }
@@ -53,16 +47,16 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
53
47
  type: 'pointer',
54
48
  target: 'selection',
55
49
  handle,
56
- ...getPointerInfo(e),
50
+ ...getPointerInfo(editor, e),
57
51
  })
58
- markEventAsHandled(e)
52
+ editor.markEventAsHandled(e)
59
53
  }
60
54
 
61
55
  // Track the last screen point
62
56
  let lastX: number, lastY: number
63
57
 
64
58
  function onPointerMove(e: React.PointerEvent) {
65
- if (wasEventAlreadyHandled(e)) return
59
+ if (editor.wasEventAlreadyHandled(e)) return
66
60
  if (e.button !== 0) return
67
61
  if (e.clientX === lastX && e.clientY === lastY) return
68
62
  lastX = e.clientX
@@ -73,12 +67,12 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
73
67
  type: 'pointer',
74
68
  target: 'selection',
75
69
  handle,
76
- ...getPointerInfo(e),
70
+ ...getPointerInfo(editor, e),
77
71
  })
78
72
  }
79
73
 
80
74
  const onPointerUp: React.PointerEventHandler = (e) => {
81
- if (wasEventAlreadyHandled(e)) return
75
+ if (editor.wasEventAlreadyHandled(e)) return
82
76
  if (e.button !== 0) return
83
77
 
84
78
  editor.dispatch({
@@ -86,7 +80,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
86
80
  type: 'pointer',
87
81
  target: 'selection',
88
82
  handle,
89
- ...getPointerInfo(e),
83
+ ...getPointerInfo(editor, e),
90
84
  })
91
85
  }
92
86
 
@@ -178,6 +178,9 @@ export class LicenseManager {
178
178
  const url = new URL(WATERMARK_TRACK_SRC)
179
179
  url.searchParams.set('version', version)
180
180
  url.searchParams.set('license_type', trackType)
181
+ if ('license' in result) {
182
+ url.searchParams.set('license_id', result.license.id)
183
+ }
181
184
 
182
185
  // eslint-disable-next-line no-restricted-globals
183
186
  fetch(url.toString())
@@ -3,7 +3,7 @@ import { memo, useRef } from 'react'
3
3
  import { useCanvasEvents } from '../hooks/useCanvasEvents'
4
4
  import { useEditor } from '../hooks/useEditor'
5
5
  import { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'
6
- import { markEventAsHandled, preventDefault } from '../utils/dom'
6
+ import { preventDefault } from '../utils/dom'
7
7
  import { runtime } from '../utils/runtime'
8
8
  import { watermarkDesktopSvg, watermarkMobileSvg } from '../watermarks'
9
9
  import { LicenseManager } from './LicenseManager'
@@ -43,11 +43,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
43
43
  isDebugMode: boolean
44
44
  isMobile: boolean
45
45
  }) {
46
+ const editor = useEditor()
46
47
  const events = useCanvasEvents()
47
48
  const ref = useRef<HTMLDivElement>(null)
48
49
  usePassThroughWheelEvents(ref)
49
50
 
50
- const url = 'https://tldraw.dev/?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'
51
+ const url =
52
+ 'https://tldraw.dev/pricing?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'
51
53
 
52
54
  return (
53
55
  <div
@@ -64,26 +66,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
64
66
  draggable={false}
65
67
  role="button"
66
68
  onPointerDown={(e) => {
67
- markEventAsHandled(e)
69
+ editor.markEventAsHandled(e)
68
70
  preventDefault(e)
69
71
  }}
70
- title="Unlicensed - click to get a license"
72
+ title="The tldraw SDK requires a license key to work in production. You can get a free 100-day trial license at tldraw.dev/pricing."
71
73
  onClick={() => runtime.openWindow(url, '_blank')}
72
- style={{
73
- position: 'absolute',
74
- pointerEvents: 'all',
75
- cursor: 'pointer',
76
- color: 'var(--tl-color-text)',
77
- opacity: 0.8,
78
- border: 0,
79
- padding: 0,
80
- backgroundColor: 'transparent',
81
- fontSize: '11px',
82
- fontWeight: '600',
83
- textAlign: 'center',
84
- }}
85
74
  >
86
- Unlicensed
75
+ Get a license for production
87
76
  </button>
88
77
  </div>
89
78
  )
@@ -127,10 +116,10 @@ const WatermarkInner = memo(function WatermarkInner({
127
116
  draggable={false}
128
117
  role="button"
129
118
  onPointerDown={(e) => {
130
- markEventAsHandled(e)
119
+ editor.markEventAsHandled(e)
131
120
  preventDefault(e)
132
121
  }}
133
- title="made with tldraw"
122
+ title="Build infinite canvas applications with the tldraw SDK. Learn more at https://tldraw.dev."
134
123
  onClick={() => runtime.openWindow(url, '_blank')}
135
124
  style={{ mask: maskCss, WebkitMask: maskCss }}
136
125
  />
@@ -142,7 +131,8 @@ const LicenseStyles = memo(function LicenseStyles() {
142
131
  const editor = useEditor()
143
132
  const className = LicenseManager.className
144
133
 
145
- const CSS = `/* ------------------- SEE LICENSE -------------------
134
+ const CSS = `
135
+ /* ------------------- SEE LICENSE -------------------
146
136
  The tldraw watermark is part of tldraw's license. It is shown for unlicensed
147
137
  or "licensed-with-watermark" users. By using this library, you agree to
148
138
  preserve the watermark's behavior, keeping it visible, unobscured, and
@@ -151,87 +141,105 @@ available to user-interaction.
151
141
  To remove the watermark, please purchase a license at tldraw.dev.
152
142
  */
153
143
 
154
- .${className} {
155
- position: absolute;
156
- bottom: max(var(--tl-space-2), env(safe-area-inset-bottom));
157
- right: max(var(--tl-space-2), env(safe-area-inset-right));
158
- width: 96px;
159
- height: 32px;
160
- display: flex;
161
- align-items: center;
162
- justify-content: center;
163
- z-index: var(--tl-layer-watermark) !important;
164
- background-color: color-mix(in srgb, var(--tl-color-background) 62%, transparent);
165
- opacity: 1;
166
- border-radius: 5px;
167
- pointer-events: all;
168
- padding: 2px;
169
- box-sizing: content-box;
144
+ .${className} {
145
+ position: absolute;
146
+ bottom: max(var(--tl-space-2), env(safe-area-inset-bottom));
147
+ right: max(var(--tl-space-2), env(safe-area-inset-right));
148
+ width: 96px;
149
+ height: 32px;
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ z-index: var(--tl-layer-watermark) !important;
154
+ background-color: color-mix(in srgb, var(--tl-color-background) 62%, transparent);
155
+ opacity: 1;
156
+ border-radius: 5px;
157
+ pointer-events: all;
158
+ padding: 2px;
159
+ box-sizing: content-box;
160
+ }
161
+
162
+ .${className} > button {
163
+ position: absolute;
164
+ width: 96px;
165
+ height: 32px;
166
+ pointer-events: all;
167
+ cursor: inherit;
168
+ color: var(--tl-color-text);
169
+ opacity: .38;
170
+ border: 0;
171
+ padding: 0;
172
+ background-color: currentColor;
173
+ }
174
+
175
+ .${className}[data-debug='true'] {
176
+ bottom: max(46px, env(safe-area-inset-bottom));
177
+ }
178
+
179
+ .${className}[data-mobile='true'] {
180
+ border-radius: 4px 0px 0px 4px;
181
+ right: max(-2px, calc(env(safe-area-inset-right) - 2px));
182
+ width: 8px;
183
+ height: 48px;
184
+ }
185
+
186
+ .${className}[data-mobile='true'] > button {
187
+ width: 8px;
188
+ height: 32px;
189
+ }
190
+
191
+ .${className}[data-unlicensed='true'] > button {
192
+ font-size: 100px;
193
+ position: absolute;
194
+ pointer-events: all;
195
+ cursor: pointer;
196
+ color: var(--tl-color-text);
197
+ opacity: 0.8;
198
+ border: 0;
199
+ padding: 0;
200
+ background-color: transparent;
201
+ font-size: 11px;
202
+ font-weight: 600;
203
+ text-align: center;
204
+ }
205
+
206
+ .${className}[data-mobile='true'][data-unlicensed='true'] > button {
207
+ display: none;
208
+ }
209
+
210
+ @media (hover: hover) {
211
+ .${className}[data-licensed='false'] > button {
212
+ pointer-events: none;
170
213
  }
171
214
 
172
- .${className} > button {
173
- position: absolute;
174
- width: 96px;
175
- height: 32px;
176
- pointer-events: all;
177
- cursor: inherit;
178
- color: var(--tl-color-text);
179
- opacity: .38;
180
- border: 0;
181
- padding: 0;
182
- background-color: currentColor;
215
+ .${className}[data-licensed='false']:hover {
216
+ background-color: var(--tl-color-background);
217
+ transition: background-color 0.2s ease-in-out;
218
+ transition-delay: 0.32s;
183
219
  }
184
220
 
185
- .${className}[data-debug='true'] {
186
- bottom: max(46px, env(safe-area-inset-bottom));
221
+ .${className}[data-licensed='false']:hover > button {
222
+ animation: ${className}_delayed_link 0.2s forwards ease-in-out;
223
+ animation-delay: 0.32s;
187
224
  }
188
225
 
189
- .${className}[data-mobile='true'] {
190
- border-radius: 4px 0px 0px 4px;
191
- right: max(-2px, calc(env(safe-area-inset-right) - 2px));
192
- width: 8px;
193
- height: 48px;
226
+ .${className}[data-licensed='false'] > button:focus-visible {
227
+ opacity: 1;
194
228
  }
229
+ }
195
230
 
196
- .${className}[data-mobile='true'] > button {
197
- width: 8px;
198
- height: 32px;
231
+ @keyframes ${className}_delayed_link {
232
+ 0% {
233
+ cursor: inherit;
234
+ opacity: .38;
235
+ pointer-events: none;
199
236
  }
200
-
201
- @media (hover: hover) {
202
- .${className} > button {
203
- pointer-events: none;
204
- }
205
-
206
- .${className}:hover {
207
- background-color: var(--tl-color-background);
208
- transition: background-color 0.2s ease-in-out;
209
- transition-delay: 0.32s;
210
- }
211
-
212
- .${className}:hover > button {
213
- animation: ${className}_delayed_link 0.2s forwards ease-in-out;
214
- animation-delay: 0.32s;
215
- }
216
-
217
- .${className} > button:focus-visible {
218
- opacity: 1;
219
- }
237
+ 100% {
238
+ cursor: pointer;
239
+ opacity: 1;
240
+ pointer-events: all;
220
241
  }
221
-
222
-
223
- @keyframes ${className}_delayed_link {
224
- 0% {
225
- cursor: inherit;
226
- opacity: .38;
227
- pointer-events: none;
228
- }
229
- 100% {
230
- cursor: pointer;
231
- opacity: 1;
232
- pointer-events: all;
233
- }
234
- }`
242
+ }`
235
243
 
236
244
  return <style nonce={editor.options.nonce}>{CSS}</style>
237
245
  })
@@ -1,18 +1,27 @@
1
- import { markEventAsHandled, wasEventAlreadyHandled } from './dom'
1
+ import { TestEditor } from '../test/TestEditor'
2
2
 
3
3
  describe('Event handling utilities', () => {
4
+ let editor: TestEditor
5
+
6
+ beforeEach(() => {
7
+ editor = new TestEditor()
8
+ })
9
+
10
+ afterEach(() => {
11
+ editor.dispose()
12
+ })
4
13
  describe('markEventAsHandled and wasEventAlreadyHandled', () => {
5
14
  it('should track events as handled', () => {
6
15
  const mockEvent = new PointerEvent('pointerdown', { pointerId: 1 })
7
16
 
8
17
  // Initially, event should not be marked as handled
9
- expect(wasEventAlreadyHandled(mockEvent)).toBe(false)
18
+ expect(editor.wasEventAlreadyHandled(mockEvent)).toBe(false)
10
19
 
11
20
  // Mark the event as handled
12
- markEventAsHandled(mockEvent)
21
+ editor.markEventAsHandled(mockEvent)
13
22
 
14
23
  // Now it should be marked as handled
15
- expect(wasEventAlreadyHandled(mockEvent)).toBe(true)
24
+ expect(editor.wasEventAlreadyHandled(mockEvent)).toBe(true)
16
25
  })
17
26
 
18
27
  it('should work with React synthetic events', () => {
@@ -20,15 +29,15 @@ describe('Event handling utilities', () => {
20
29
  const syntheticEvent = { nativeEvent }
21
30
 
22
31
  // Initially not handled
23
- expect(wasEventAlreadyHandled(syntheticEvent)).toBe(false)
24
- expect(wasEventAlreadyHandled(nativeEvent)).toBe(false)
32
+ expect(editor.wasEventAlreadyHandled(syntheticEvent)).toBe(false)
33
+ expect(editor.wasEventAlreadyHandled(nativeEvent)).toBe(false)
25
34
 
26
35
  // Mark synthetic event as handled
27
- markEventAsHandled(syntheticEvent)
36
+ editor.markEventAsHandled(syntheticEvent)
28
37
 
29
38
  // Both synthetic and native should be marked as handled
30
- expect(wasEventAlreadyHandled(syntheticEvent)).toBe(true)
31
- expect(wasEventAlreadyHandled(nativeEvent)).toBe(true)
39
+ expect(editor.wasEventAlreadyHandled(syntheticEvent)).toBe(true)
40
+ expect(editor.wasEventAlreadyHandled(nativeEvent)).toBe(true)
32
41
  })
33
42
 
34
43
  it('should handle multiple different events independently', () => {
@@ -37,18 +46,18 @@ describe('Event handling utilities', () => {
37
46
  const event3 = new MouseEvent('click')
38
47
 
39
48
  // Mark only event1 as handled
40
- markEventAsHandled(event1)
49
+ editor.markEventAsHandled(event1)
41
50
 
42
- expect(wasEventAlreadyHandled(event1)).toBe(true)
43
- expect(wasEventAlreadyHandled(event2)).toBe(false)
44
- expect(wasEventAlreadyHandled(event3)).toBe(false)
51
+ expect(editor.wasEventAlreadyHandled(event1)).toBe(true)
52
+ expect(editor.wasEventAlreadyHandled(event2)).toBe(false)
53
+ expect(editor.wasEventAlreadyHandled(event3)).toBe(false)
45
54
 
46
55
  // Mark event2 as handled
47
- markEventAsHandled(event2)
56
+ editor.markEventAsHandled(event2)
48
57
 
49
- expect(wasEventAlreadyHandled(event1)).toBe(true)
50
- expect(wasEventAlreadyHandled(event2)).toBe(true)
51
- expect(wasEventAlreadyHandled(event3)).toBe(false)
58
+ expect(editor.wasEventAlreadyHandled(event1)).toBe(true)
59
+ expect(editor.wasEventAlreadyHandled(event2)).toBe(true)
60
+ expect(editor.wasEventAlreadyHandled(event3)).toBe(false)
52
61
  })
53
62
 
54
63
  it('should not interfere with event properties', () => {
@@ -59,7 +68,7 @@ describe('Event handling utilities', () => {
59
68
  })
60
69
 
61
70
  // Mark as handled
62
- markEventAsHandled(event)
71
+ editor.markEventAsHandled(event)
63
72
 
64
73
  // Event properties should remain unchanged
65
74
  expect(event.pointerId).toBe(1)
@@ -78,17 +87,17 @@ describe('Event handling utilities', () => {
78
87
  ],
79
88
  })
80
89
 
81
- expect(wasEventAlreadyHandled(touchEvent)).toBe(false)
82
- markEventAsHandled(touchEvent)
83
- expect(wasEventAlreadyHandled(touchEvent)).toBe(true)
90
+ expect(editor.wasEventAlreadyHandled(touchEvent)).toBe(false)
91
+ editor.markEventAsHandled(touchEvent)
92
+ expect(editor.wasEventAlreadyHandled(touchEvent)).toBe(true)
84
93
  })
85
94
 
86
95
  it('should work with keyboard events', () => {
87
96
  const keyEvent = new KeyboardEvent('keydown', { key: 'Enter' })
88
97
 
89
- expect(wasEventAlreadyHandled(keyEvent)).toBe(false)
90
- markEventAsHandled(keyEvent)
91
- expect(wasEventAlreadyHandled(keyEvent)).toBe(true)
98
+ expect(editor.wasEventAlreadyHandled(keyEvent)).toBe(false)
99
+ editor.markEventAsHandled(keyEvent)
100
+ expect(editor.wasEventAlreadyHandled(keyEvent)).toBe(true)
92
101
  })
93
102
  })
94
103
  })
@@ -81,43 +81,13 @@ export function releasePointerCapture(
81
81
  /**
82
82
  * Calls `event.stopPropagation()`.
83
83
  *
84
- * @deprecated Use {@link markEventAsHandled} instead, or manually call `event.stopPropagation()` if
84
+ * @deprecated Use {@link Editor.markEventAsHandled} instead, or manually call `event.stopPropagation()` if
85
85
  * that's what you really want.
86
86
  *
87
87
  * @public
88
88
  */
89
89
  export const stopEventPropagation = (e: any) => e.stopPropagation()
90
90
 
91
- const handledEvents = new WeakSet<Event>()
92
-
93
- /**
94
- * In tldraw, events are sometimes handled by multiple components. For example, the shapes might
95
- * have events, but the canvas handles events too. The way that the canvas handles events can
96
- * interfere with the with the shapes event handlers - for example, it calls `.preventDefault()` on
97
- * `pointerDown`, which also prevents `click` events from firing on the shapes.
98
- *
99
- * You can use `.stopPropagation()` to prevent the event from propagating to the rest of the DOM,
100
- * but that can impact non-tldraw event handlers set up elsewhere. By using `markEventAsHandled`,
101
- * you'll stop other parts of tldraw from handling the event without impacting other, non-tldraw
102
- * event handlers. See also {@link wasEventAlreadyHandled}.
103
- *
104
- * @public
105
- */
106
- export function markEventAsHandled(e: Event | { nativeEvent: Event }) {
107
- const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
108
- handledEvents.add(nativeEvent)
109
- }
110
-
111
- /**
112
- * Checks if an event has already been handled. See {@link markEventAsHandled}.
113
- *
114
- * @public
115
- */
116
- export function wasEventAlreadyHandled(e: Event | { nativeEvent: Event }) {
117
- const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
118
- return handledEvents.has(nativeEvent)
119
- }
120
-
121
91
  /** @internal */
122
92
  export const setStyleProperty = (
123
93
  elm: HTMLElement | null,
@@ -1,9 +1,9 @@
1
- import { markEventAsHandled } from './dom'
1
+ import { Editor } from '../editor/Editor'
2
2
  import { isAccelKey } from './keyboard'
3
3
 
4
4
  /** @public */
5
- export function getPointerInfo(e: React.PointerEvent | PointerEvent) {
6
- markEventAsHandled(e)
5
+ export function getPointerInfo(editor: Editor, e: React.PointerEvent | PointerEvent) {
6
+ editor.markEventAsHandled(e)
7
7
 
8
8
  return {
9
9
  point: {
@@ -162,7 +162,13 @@ function getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(
162
162
  const parentPageTransform = editor.getShapePageTransform(shape)
163
163
  const parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)
164
164
 
165
- const parentPageMaskVertices = editor.getShapeMask(shape)
165
+ const _shape = editor.getShape(shape)
166
+ if (!_shape) return EMPTY_ARRAY
167
+
168
+ const pageTransform = editor.getShapePageTransform(shape)
169
+ const clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape)
170
+
171
+ const parentPageMaskVertices = clipPath ? pageTransform.applyToPoints(clipPath) : undefined
166
172
  const parentPagePolygon = parentPageMaskVertices
167
173
  ? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)
168
174
  : parentPageCorners
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.2e83e38fb91b'
4
+ export const version = '3.16.0-canary.344cec0354f3'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-09-15T17:14:42.089Z',
8
- patch: '2025-09-15T17:14:42.089Z',
7
+ minor: '2025-09-18T14:24:07.314Z',
8
+ patch: '2025-09-18T14:24:07.314Z',
9
9
  }