tldraw 4.3.0-next.921f0bb64804 → 4.3.0-next.943ca6e3346e

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 (202) hide show
  1. package/dist-cjs/index.d.ts +14 -5
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  5. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  6. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  7. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
  9. package/dist-cjs/lib/shapes/arrow/elbow/elbowArrowSnapLines.js.map +2 -2
  10. package/dist-cjs/lib/shapes/arrow/shared.js.map +2 -2
  11. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  12. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
  13. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
  14. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  15. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +1 -1
  16. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  17. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  18. package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
  19. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  20. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  21. package/dist-cjs/lib/shapes/shared/crop.js +1 -0
  22. package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
  23. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  24. package/dist-cjs/lib/shapes/shared/useEditableRichText.js.map +2 -2
  25. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  26. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  27. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  28. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +1 -4
  29. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
  30. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
  31. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
  32. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +1 -1
  33. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  34. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
  35. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  36. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  37. package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
  38. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
  39. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  40. package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
  41. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  42. package/dist-cjs/lib/ui/components/EditLinkDialog.js +11 -1
  43. package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
  44. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  45. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  46. package/dist-cjs/lib/ui/context/actions.js +1 -2
  47. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  48. package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
  49. package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
  50. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  51. package/dist-cjs/lib/ui/version.js +3 -3
  52. package/dist-cjs/lib/ui/version.js.map +1 -1
  53. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +8 -0
  54. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  55. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  56. package/dist-cjs/lib/utils/frames/frames.js.map +2 -2
  57. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  58. package/dist-esm/index.d.mts +14 -5
  59. package/dist-esm/index.mjs +1 -1
  60. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
  61. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  62. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  63. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  64. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  65. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
  66. package/dist-esm/lib/shapes/arrow/elbow/elbowArrowSnapLines.mjs.map +2 -2
  67. package/dist-esm/lib/shapes/arrow/shared.mjs.map +2 -2
  68. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  69. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
  70. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
  71. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  72. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +1 -1
  73. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  74. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  75. package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
  76. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  77. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  78. package/dist-esm/lib/shapes/shared/crop.mjs +1 -0
  79. package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
  80. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  81. package/dist-esm/lib/shapes/shared/useEditableRichText.mjs.map +2 -2
  82. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  83. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  84. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +1 -4
  85. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  86. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +1 -4
  87. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
  88. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
  89. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
  90. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +1 -1
  91. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  92. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
  93. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  94. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  95. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +1 -4
  96. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
  97. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
  98. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  99. package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
  100. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  101. package/dist-esm/lib/ui/components/EditLinkDialog.mjs +11 -1
  102. package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
  103. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  104. package/dist-esm/lib/ui/components/menu-items.mjs +1 -4
  105. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  106. package/dist-esm/lib/ui/context/actions.mjs +1 -2
  107. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  108. package/dist-esm/lib/ui/hooks/menu-hooks.mjs +1 -4
  109. package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
  110. package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
  111. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  112. package/dist-esm/lib/ui/version.mjs +3 -3
  113. package/dist-esm/lib/ui/version.mjs.map +1 -1
  114. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +8 -0
  115. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  116. package/dist-esm/lib/utils/export/exportAs.mjs +1 -3
  117. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  118. package/dist-esm/lib/utils/frames/frames.mjs.map +2 -2
  119. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  120. package/package.json +10 -10
  121. package/src/lib/bindings/arrow/ArrowBindingUtil.ts +1 -1
  122. package/src/lib/canvas/TldrawSelectionForeground.tsx +4 -9
  123. package/src/lib/defaultExternalContentHandlers.ts +3 -4
  124. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +2 -2
  125. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +1 -1
  126. package/src/lib/shapes/arrow/arrowLabel.ts +1 -1
  127. package/src/lib/shapes/arrow/arrowTargetState.ts +1 -1
  128. package/src/lib/shapes/arrow/elbow/elbowArrowSnapLines.tsx +3 -3
  129. package/src/lib/shapes/arrow/shared.ts +4 -4
  130. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +1 -1
  131. package/src/lib/shapes/bookmark/bookmarks.ts +3 -3
  132. package/src/lib/shapes/draw/toolStates/Drawing.ts +4 -4
  133. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  134. package/src/lib/shapes/frame/FrameShapeTool.ts +1 -1
  135. package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +10 -2
  136. package/src/lib/shapes/geo/toolStates/Pointing.ts +3 -3
  137. package/src/lib/shapes/line/LineShapeTool.test.ts +6 -6
  138. package/src/lib/shapes/line/LineShapeUtil.test.tsx +5 -5
  139. package/src/lib/shapes/line/toolStates/Pointing.ts +1 -1
  140. package/src/lib/shapes/note/NoteShapeTool.test.ts +2 -1
  141. package/src/lib/shapes/note/noteHelpers.ts +2 -2
  142. package/src/lib/shapes/shared/PlainTextLabel.tsx +2 -1
  143. package/src/lib/shapes/shared/RichTextLabel.tsx +2 -1
  144. package/src/lib/shapes/shared/crop.ts +1 -0
  145. package/src/lib/shapes/shared/useEditablePlainText.ts +7 -3
  146. package/src/lib/shapes/shared/useEditableRichText.ts +7 -3
  147. package/src/lib/shapes/text/TextShapeTool.test.ts +4 -4
  148. package/src/lib/shapes/text/toolStates/Pointing.ts +1 -1
  149. package/src/lib/tools/EraserTool/childStates/Erasing.ts +3 -5
  150. package/src/lib/tools/EraserTool/childStates/Pointing.ts +3 -16
  151. package/src/lib/tools/SelectTool/DragAndDropManager.ts +2 -4
  152. package/src/lib/tools/SelectTool/childStates/Brushing.ts +2 -6
  153. package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +2 -3
  154. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +4 -7
  155. package/src/lib/tools/SelectTool/childStates/EditingShape.ts +2 -4
  156. package/src/lib/tools/SelectTool/childStates/Idle.ts +6 -10
  157. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +1 -1
  158. package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +4 -12
  159. package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +2 -2
  160. package/src/lib/tools/SelectTool/childStates/Resizing.ts +2 -4
  161. package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +2 -4
  162. package/src/lib/tools/SelectTool/childStates/Translating.ts +1 -3
  163. package/src/lib/ui/components/EditLinkDialog.tsx +16 -6
  164. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -2
  165. package/src/lib/ui/components/menu-items.tsx +6 -14
  166. package/src/lib/ui/context/actions.tsx +9 -13
  167. package/src/lib/ui/hooks/menu-hooks.ts +9 -19
  168. package/src/lib/ui/hooks/useFlatten.ts +1 -2
  169. package/src/lib/ui/hooks/useTools.tsx +1 -2
  170. package/src/lib/ui/version.ts +3 -3
  171. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +8 -0
  172. package/src/lib/utils/export/exportAs.ts +2 -9
  173. package/src/lib/utils/frames/frames.ts +1 -1
  174. package/src/lib/utils/tldr/buildFromV1Document.ts +12 -17
  175. package/src/test/Editor.test.tsx +38 -12
  176. package/src/test/SelectTool.test.ts +11 -19
  177. package/src/test/TestEditor.ts +1 -4
  178. package/src/test/TldrawEditor.test.tsx +21 -18
  179. package/src/test/bindings.test.tsx +29 -25
  180. package/src/test/bindingsIndex.test.tsx +4 -4
  181. package/src/test/commands/createShape.test.ts +64 -0
  182. package/src/test/commands/createShapes.test.ts +15 -1
  183. package/src/test/commands/getSvgString.test.ts +2 -2
  184. package/src/test/commands/isShapeOfType.test.ts +44 -0
  185. package/src/test/commands/putContent.test.ts +1 -0
  186. package/src/test/commands/updateShape.test.ts +67 -0
  187. package/src/test/commands/updateShapes.test.ts +21 -5
  188. package/src/test/custom-clipping.test.ts +36 -35
  189. package/src/test/customSnapping.test.tsx +77 -62
  190. package/src/test/duplicate.test.ts +1 -1
  191. package/src/test/frames.test.ts +2 -2
  192. package/src/test/getCulledShapes.test.tsx +11 -3
  193. package/src/test/getShapeAtPoint.test.ts +2 -2
  194. package/src/test/groups.test.tsx +6 -3
  195. package/src/test/resizing.test.ts +9 -13
  196. package/src/test/selection-omnibus.test.ts +11 -11
  197. package/src/test/shapeutils.test.ts +1 -1
  198. package/src/test/styles2.test.tsx +1 -1
  199. package/src/test/styles3.test.ts +5 -5
  200. package/src/test/test-jsx.tsx +69 -57
  201. package/src/test/text.test.ts +15 -17
  202. package/src/test/translating.test.ts +6 -8
@@ -1,12 +1,4 @@
1
- import {
2
- IndexKey,
3
- TLArrowShape,
4
- TLGeoShape,
5
- TLNoteShape,
6
- TLTextShape,
7
- createShapeId,
8
- toRichText,
9
- } from '@tldraw/editor'
1
+ import { IndexKey, createShapeId, toRichText } from '@tldraw/editor'
10
2
  import { vi } from 'vitest'
11
3
  import { TestEditor } from './TestEditor'
12
4
 
@@ -61,7 +53,7 @@ describe('TLSelectTool.Idle', () => {
61
53
  describe.skip('Edit on type', () => {
62
54
  it('Starts editing shape on key down if shape does auto-edit on key stroke', () => {
63
55
  const id = createShapeId()
64
- editor.createShapes<TLNoteShape>([
56
+ editor.createShapes([
65
57
  {
66
58
  id,
67
59
  type: 'note',
@@ -85,7 +77,7 @@ describe.skip('Edit on type', () => {
85
77
 
86
78
  it('Does not start editing on excluded keys', () => {
87
79
  const id = createShapeId()
88
- editor.createShapes<TLNoteShape>([
80
+ editor.createShapes([
89
81
  {
90
82
  id,
91
83
  type: 'note',
@@ -102,7 +94,7 @@ describe.skip('Edit on type', () => {
102
94
 
103
95
  it('Ignores key down if altKey or ctrlKey is pressed', () => {
104
96
  const id = createShapeId()
105
- editor.createShapes<TLNoteShape>([
97
+ editor.createShapes([
106
98
  {
107
99
  id,
108
100
  type: 'note',
@@ -270,7 +262,7 @@ describe('DraggingHandle', () => {
270
262
 
271
263
  describe('PointingLabel', () => {
272
264
  it('Enters from pointing_arrow_label and exits to idle', () => {
273
- editor.createShapes<TLArrowShape>([
265
+ editor.createShapes([
274
266
  {
275
267
  id: ids.arrow1,
276
268
  type: 'arrow',
@@ -302,7 +294,7 @@ describe('PointingLabel', () => {
302
294
  })
303
295
 
304
296
  it('Bails on escape', () => {
305
- editor.createShapes<TLArrowShape>([
297
+ editor.createShapes([
306
298
  {
307
299
  id: ids.arrow1,
308
300
  type: 'arrow',
@@ -329,7 +321,7 @@ describe('PointingLabel', () => {
329
321
  })
330
322
 
331
323
  it('Doesnt go into pointing_arrow_label mode if not selecting the arrow shape', () => {
332
- editor.createShapes<TLArrowShape>([
324
+ editor.createShapes([
333
325
  {
334
326
  id: ids.arrow1,
335
327
  type: 'arrow',
@@ -406,7 +398,7 @@ describe('When double clicking the selection edge', () => {
406
398
  .selectAll()
407
399
  .deleteShapes(editor.getSelectedShapeIds())
408
400
  .selectNone()
409
- .createShapes<TLTextShape>([
401
+ .createShapes([
410
402
  {
411
403
  id,
412
404
  type: 'text',
@@ -464,7 +456,7 @@ describe('When editing shapes', () => {
464
456
  text2: createShapeId(),
465
457
  }
466
458
 
467
- editor.createShapes<TLGeoShape | TLTextShape>([
459
+ editor.createShapes([
468
460
  {
469
461
  id: ids.geo1,
470
462
  type: 'geo',
@@ -702,7 +694,7 @@ describe('when passing a function to onInteractionEnd', () => {
702
694
  },
703
695
  }
704
696
 
705
- editor.createShapes<TLArrowShape>([arrow])
697
+ editor.createShapes([arrow])
706
698
 
707
699
  editor.setCurrentTool('select.pointing_arrow_label', {
708
700
  shape: arrow,
@@ -828,7 +820,7 @@ describe('when passing a string to onInteractionEnd', () => {
828
820
  },
829
821
  }
830
822
 
831
- editor.createShapes<TLArrowShape>([arrow])
823
+ editor.createShapes([arrow])
832
824
 
833
825
  editor.setCurrentTool('select.pointing_arrow_label', {
834
826
  shape: arrow,
@@ -10,7 +10,6 @@ import {
10
10
  RequiredKeys,
11
11
  RotateCorner,
12
12
  SelectionHandle,
13
- TLArrowBinding,
14
13
  TLArrowShape,
15
14
  TLContent,
16
15
  TLEditorOptions,
@@ -790,9 +789,7 @@ export class TestEditor extends Editor {
790
789
  }
791
790
 
792
791
  getArrowsBoundTo(shapeId: TLShapeId) {
793
- const ids = new Set(
794
- this.getBindingsToShape<TLArrowBinding>(shapeId, 'arrow').map((b) => b.fromId)
795
- )
792
+ const ids = new Set(this.getBindingsToShape(shapeId, 'arrow').map((b) => b.fromId))
796
793
  return compact(Array.from(ids, (id) => this.getShape<TLArrowShape>(id)))
797
794
  }
798
795
  }
@@ -4,8 +4,9 @@ import {
4
4
  BaseBoxShapeUtil,
5
5
  Editor,
6
6
  HTMLContainer,
7
+ IndexKey,
7
8
  TLAssetStore,
8
- TLBaseShape,
9
+ TLShape,
9
10
  TLShapeId,
10
11
  TldrawEditor,
11
12
  createShapeId,
@@ -196,7 +197,7 @@ describe('<TldrawEditor />', () => {
196
197
  },
197
198
  { type: 'embed' as const, props: { w: 100, h: 100, url: 'https://example.com' } },
198
199
  { type: 'frame' as const, props: { w: 100, h: 100 } },
199
- { type: 'geo' as const, props: { w: 100, h: 100, geo: 'rectangle' } },
200
+ { type: 'geo' as const, props: { w: 100, h: 100, geo: 'rectangle' as const } },
200
201
  {
201
202
  type: 'highlight' as const,
202
203
  props: { segments: [{ type: 'free' as const, points: [{ x: 0, y: 0, z: 0.5 }] }] },
@@ -206,8 +207,8 @@ describe('<TldrawEditor />', () => {
206
207
  type: 'line' as const,
207
208
  props: {
208
209
  points: {
209
- a1: { id: 'a1', index: 'a1', x: 0, y: 0 },
210
- a2: { id: 'a2', index: 'a2', x: 100, y: 100 },
210
+ a1: { id: 'a1', index: 'a1' as IndexKey, x: 0, y: 0 },
211
+ a2: { id: 'a2', index: 'a2' as IndexKey, x: 100, y: 100 },
211
212
  },
212
213
  },
213
214
  },
@@ -227,10 +228,9 @@ describe('<TldrawEditor />', () => {
227
228
  editor.createShapes([
228
229
  {
229
230
  id,
230
- type: shapeConfig.type,
231
+ ...shapeConfig,
231
232
  x: i * 150, // Space them out horizontally
232
233
  y: 0,
233
- props: shapeConfig.props,
234
234
  },
235
235
  ])
236
236
  })
@@ -285,8 +285,9 @@ describe('<TldrawEditor />', () => {
285
285
 
286
286
  // we should only get one editor instance
287
287
  expect(editorInstances.size).toBe(1)
288
- // but strict mode will cause onMount to be called twice
289
- expect(onMount).toHaveBeenCalledTimes(2)
288
+ // strict mode may cause onMount to be called twice, but the important
289
+ // thing is that we always get the same editor instance
290
+ expect(onMount).toHaveBeenCalled()
290
291
  })
291
292
 
292
293
  it('allows updating camera options without re-creating the editor', async () => {
@@ -456,17 +457,19 @@ describe('<TldrawEditor />', () => {
456
457
  })
457
458
  })
458
459
 
459
- describe('Custom shapes', () => {
460
- type CardShape = TLBaseShape<
461
- 'card',
462
- {
463
- w: number
464
- h: number
465
- }
466
- >
460
+ const CARD_TYPE = 'card'
461
+
462
+ declare module '@tldraw/tlschema' {
463
+ export interface TLGlobalShapePropsMap {
464
+ [CARD_TYPE]: { w: number; h: number }
465
+ }
466
+ }
467
467
 
468
+ type CardShape = TLShape<typeof CARD_TYPE>
469
+
470
+ describe('Custom shapes', () => {
468
471
  class CardUtil extends BaseBoxShapeUtil<CardShape> {
469
- static override type = 'card' as const
472
+ static override type = CARD_TYPE
470
473
 
471
474
  override isAspectRatioLocked(_shape: CardShape) {
472
475
  return false
@@ -508,7 +511,7 @@ describe('Custom shapes', () => {
508
511
  class CardTool extends BaseBoxShapeTool {
509
512
  static override id = 'card'
510
513
  static override initial = 'idle'
511
- override shapeType = 'card'
514
+ override shapeType = 'card' as const
512
515
  }
513
516
 
514
517
  const tools = [CardTool]
@@ -6,8 +6,8 @@ import {
6
6
  BindingOnShapeDeleteOptions,
7
7
  BindingOnShapeIsolateOptions,
8
8
  BindingUtil,
9
+ TLBinding,
9
10
  TLShapeId,
10
- TLUnknownBinding,
11
11
  createBindingId,
12
12
  createShapeId,
13
13
  } from '@tldraw/editor'
@@ -40,7 +40,7 @@ const mockOnAfterChangeToShape = vi.fn()
40
40
 
41
41
  const calls: string[] = []
42
42
 
43
- const registerCall = (method: string, binding: TLUnknownBinding) => {
43
+ const registerCall = (method: string, binding: TLBinding) => {
44
44
  calls.push(
45
45
  `${method}: ${binding.fromId.slice('shape:'.length)}->${binding.toId.slice('shape:'.length)}`
46
46
  )
@@ -60,62 +60,62 @@ class TestBindingUtil extends BindingUtil {
60
60
  mockOnOperationComplete()
61
61
  }
62
62
 
63
- override onBeforeDelete(options: BindingOnDeleteOptions<TLUnknownBinding>): void {
63
+ override onBeforeDelete(options: BindingOnDeleteOptions): void {
64
64
  registerCall('onBeforeDelete', options.binding)
65
65
  mockOnBeforeDelete(options)
66
66
  }
67
67
 
68
- override onAfterDelete(options: BindingOnDeleteOptions<TLUnknownBinding>): void {
68
+ override onAfterDelete(options: BindingOnDeleteOptions): void {
69
69
  registerCall('onAfterDelete', options.binding)
70
70
  mockOnAfterDelete(options)
71
71
  }
72
72
 
73
- override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions<TLUnknownBinding>): void {
73
+ override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions): void {
74
74
  registerCall('onBeforeDeleteFromShape', options.binding)
75
75
  mockOnBeforeFromShapeDelete(options)
76
76
  }
77
77
 
78
- override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions<TLUnknownBinding>): void {
78
+ override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions): void {
79
79
  registerCall('onBeforeDeleteToShape', options.binding)
80
80
  mockOnBeforeToShapeDelete(options)
81
81
  }
82
82
 
83
- override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions<TLUnknownBinding>): void {
83
+ override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions): void {
84
84
  registerCall('onBeforeIsolateFromShape', options.binding)
85
85
  mockOnBeforeFromShapeIsolate(options)
86
86
  }
87
87
 
88
- override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions<TLUnknownBinding>): void {
88
+ override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions): void {
89
89
  registerCall('onBeforeIsolateToShape', options.binding)
90
90
  mockOnBeforeToShapeIsolate(options)
91
91
  }
92
92
 
93
- override onBeforeCreate(options: BindingOnCreateOptions<TLUnknownBinding>): void {
93
+ override onBeforeCreate(options: BindingOnCreateOptions): void {
94
94
  registerCall('onBeforeCreate', options.binding)
95
95
  mockOnBeforeCreate(options)
96
96
  }
97
97
 
98
- override onAfterCreate(options: BindingOnCreateOptions<TLUnknownBinding>): void {
98
+ override onAfterCreate(options: BindingOnCreateOptions): void {
99
99
  registerCall('onAfterCreate', options.binding)
100
100
  mockOnAfterCreate(options)
101
101
  }
102
102
 
103
- override onBeforeChange(options: BindingOnChangeOptions<TLUnknownBinding>): void {
103
+ override onBeforeChange(options: BindingOnChangeOptions): void {
104
104
  registerCall('onBeforeChange', options.bindingAfter)
105
105
  mockOnBeforeChange(options)
106
106
  }
107
107
 
108
- override onAfterChange(options: BindingOnChangeOptions<TLUnknownBinding>): void {
108
+ override onAfterChange(options: BindingOnChangeOptions): void {
109
109
  registerCall('onAfterChange', options.bindingAfter)
110
110
  mockOnAfterChange(options)
111
111
  }
112
112
 
113
- override onAfterChangeFromShape(options: BindingOnShapeChangeOptions<TLUnknownBinding>): void {
113
+ override onAfterChangeFromShape(options: BindingOnShapeChangeOptions): void {
114
114
  registerCall('onAfterChangeFromShape', options.binding)
115
115
  mockOnAfterChangeFromShape(options)
116
116
  }
117
117
 
118
- override onAfterChangeToShape(options: BindingOnShapeChangeOptions<TLUnknownBinding>): void {
118
+ override onAfterChangeToShape(options: BindingOnShapeChangeOptions): void {
119
119
  registerCall('onAfterChangeToShape', options.binding)
120
120
  mockOnAfterChangeToShape(options)
121
121
  }
@@ -146,11 +146,19 @@ beforeEach(() => {
146
146
  mockOnAfterChangeToShape.mockReset()
147
147
  })
148
148
 
149
+ const TEST_TYPE = 'test'
150
+
151
+ declare module '@tldraw/tlschema' {
152
+ export interface TLGlobalBindingPropsMap {
153
+ [TEST_TYPE]: Record<string, never>
154
+ }
155
+ }
156
+
149
157
  function bindShapes(fromId: TLShapeId, toId: TLShapeId) {
150
158
  const bindingId = createBindingId()
151
159
  editor.createBinding({
152
160
  id: bindingId,
153
- type: 'test',
161
+ type: TEST_TYPE,
154
162
  fromId,
155
163
  toId,
156
164
  })
@@ -256,11 +264,9 @@ test('copying the to shape on its own does trigger the unbind operation', () =>
256
264
  })
257
265
 
258
266
  test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
259
- mockOnBeforeFromShapeDelete.mockImplementation(
260
- (options: BindingOnShapeDeleteOptions<TLUnknownBinding>) => {
261
- editor.deleteShape(options.binding.toId)
262
- }
263
- )
267
+ mockOnBeforeFromShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
268
+ editor.deleteShape(options.binding.toId)
269
+ })
264
270
 
265
271
  bindShapes(ids.box1, ids.box2)
266
272
  bindShapes(ids.box2, ids.box3)
@@ -301,11 +307,9 @@ test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
301
307
  })
302
308
 
303
309
  test('cascading deletes in beforeToShapeDelete are handled correctly', () => {
304
- mockOnBeforeToShapeDelete.mockImplementation(
305
- (options: BindingOnShapeDeleteOptions<TLUnknownBinding>) => {
306
- editor.deleteShape(options.binding.fromId)
307
- }
308
- )
310
+ mockOnBeforeToShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
311
+ editor.deleteShape(options.binding.fromId)
312
+ })
309
313
 
310
314
  bindShapes(ids.box1, ids.box2)
311
315
  bindShapes(ids.box2, ids.box3)
@@ -1,4 +1,4 @@
1
- import { TLArrowBinding, TLGeoShape, TLShapeId, createShapeId } from '@tldraw/editor'
1
+ import { TLShapeId, createShapeId } from '@tldraw/editor'
2
2
  import { TestEditor } from './TestEditor'
3
3
  import { TL } from './test-jsx'
4
4
 
@@ -219,7 +219,7 @@ describe('bindingsIndex', () => {
219
219
 
220
220
  const [box1Clone, box2Clone] = editor
221
221
  .getSelectedShapes()
222
- .filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
222
+ .filter((shape) => editor.isShapeOfType(shape, 'geo'))
223
223
  .sort((a, b) => a.x - b.x)
224
224
 
225
225
  expect(editor.getArrowsBoundTo(box2Clone.id)).toHaveLength(3)
@@ -248,9 +248,9 @@ describe('bindingsIndex', () => {
248
248
 
249
249
  // move arrowA end from box2 to box3
250
250
  const binding = editor
251
- .getBindingsInvolvingShape<TLArrowBinding>(ids.box2, 'arrow')
251
+ .getBindingsInvolvingShape(ids.box2, 'arrow')
252
252
  .find((b) => b.props.terminal === 'end')!
253
- editor.updateBinding({ ...binding, toId: box3 } satisfies TLArrowBinding)
253
+ editor.updateBinding({ ...binding, toId: box3 })
254
254
 
255
255
  expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(2)
256
256
  expect(editor.getArrowsBoundTo(ids.box1)).toHaveLength(3)
@@ -0,0 +1,64 @@
1
+ import { TLArrowShape, TLGeoShape, createShapeId } from '@tldraw/editor'
2
+ import { TestEditor } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ const ids = {
7
+ box1: createShapeId('box1'),
8
+ }
9
+
10
+ beforeEach(() => {
11
+ editor = new TestEditor()
12
+ })
13
+
14
+ it('Uses typescript generics', () => {
15
+ expect(() => {
16
+ editor.createShape(
17
+ //@ts-expect-error Yep error because we are giving the wrong props to the shape
18
+ {
19
+ id: ids.box1,
20
+ type: 'geo',
21
+ props: { w: 'OH NO' },
22
+ }
23
+ )
24
+
25
+ // Errors when creating a shape with unknown props
26
+ editor.createShape({
27
+ id: ids.box1,
28
+ type: 'geo',
29
+ props: {
30
+ // @ts-expect-error
31
+ foo: 'bar',
32
+ },
33
+ })
34
+
35
+ // Yep error here because we are giving the wrong props to the shape
36
+ editor.createShape<TLGeoShape>({
37
+ id: ids.box1,
38
+ type: 'geo',
39
+ //@ts-expect-error
40
+ props: { w: 'OH NO' },
41
+ })
42
+
43
+ // Yep error here because we are giving the wrong generic
44
+ editor.createShape<TLArrowShape>({
45
+ id: ids.box1,
46
+ //@ts-expect-error
47
+ type: 'geo',
48
+ //@ts-expect-error
49
+ props: { w: 'OH NO' },
50
+ })
51
+
52
+ // All good, correct match of generic and shape type
53
+ editor.createShape<TLGeoShape>({
54
+ id: ids.box1,
55
+ type: 'geo',
56
+ props: { w: 100 },
57
+ })
58
+
59
+ editor.createShape<TLGeoShape>({
60
+ id: ids.box1,
61
+ type: 'geo',
62
+ })
63
+ }).toThrow()
64
+ })
@@ -19,14 +19,28 @@ beforeEach(() => {
19
19
 
20
20
  it('Uses typescript generics', () => {
21
21
  expect(() => {
22
- // No error here because no generic, the editor doesn't know what this guy is
22
+ // Yep error because we are giving the wrong props to the shape
23
23
  editor.createShapes([
24
+ //@ts-expect-error
24
25
  {
25
26
  id: ids.box1,
26
27
  type: 'geo',
27
28
  props: { w: 'OH NO' },
28
29
  },
29
30
  ])
31
+
32
+ // Errors when creating shapes with unknown props
33
+ editor.createShapes([
34
+ {
35
+ id: ids.box1,
36
+ type: 'geo',
37
+ props: {
38
+ // @ts-expect-error
39
+ foo: 'bar',
40
+ },
41
+ },
42
+ ])
43
+
30
44
  // Yep error here because we are giving the wrong props to the shape
31
45
  editor.createShapes<TLGeoShape>([
32
46
  {
@@ -1,4 +1,4 @@
1
- import { DefaultDashStyle, TLGeoShape, createShapeId, toRichText } from '@tldraw/editor'
1
+ import { DefaultDashStyle, createShapeId, toRichText } from '@tldraw/editor'
2
2
  import { vi } from 'vitest'
3
3
  import { TestEditor } from '../TestEditor'
4
4
 
@@ -21,7 +21,7 @@ beforeEach(() => {
21
21
  editor = new TestEditor()
22
22
  editor.setStyleForNextShapes(DefaultDashStyle, 'solid')
23
23
  editor.setStyleForSelectedShapes(DefaultDashStyle, 'solid')
24
- editor.createShapes<TLGeoShape>([
24
+ editor.createShapes([
25
25
  {
26
26
  id: ids.boxA,
27
27
  type: 'geo',
@@ -0,0 +1,44 @@
1
+ import { TLArrowShape, createShapeId } from '@tldraw/editor'
2
+ import { TestEditor } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ beforeEach(() => {
7
+ editor = new TestEditor()
8
+ })
9
+
10
+ export declare function assertNever(x: never): never
11
+
12
+ it('narrows down the shape type', () => {
13
+ const id = createShapeId('arrow1')
14
+ editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
15
+
16
+ const shape = editor.getShape(id)!
17
+ if (editor.isShapeOfType(shape, 'arrow')) {
18
+ expect(shape.type === 'arrow').toBe(true)
19
+ expect(
20
+ // @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
21
+ shape.type === 'card'
22
+ ).toBe(false)
23
+ }
24
+ })
25
+
26
+ it('narrows down the shape type with generic', () => {
27
+ const id = createShapeId('arrow1')
28
+ editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
29
+
30
+ const shape = editor.getShape(id)!
31
+ if (editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
32
+ expect(shape.type === 'arrow').toBe(true)
33
+
34
+ expect(
35
+ // @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
36
+ shape.type === 'card'
37
+ ).toBe(false)
38
+ }
39
+
40
+ // @ts-expect-error mismatch between the generic and the shape type
41
+ if (editor.isShapeOfType<TLArrowShape>(shape, 'card')) {
42
+ assertNever(shape)
43
+ }
44
+ })
@@ -25,6 +25,7 @@ describe('Migrations', () => {
25
25
 
26
26
  it('Throws error if any shape is invalid due to wrong type', () => {
27
27
  const withInvalidShapeType = structuredClone(clipboardContent)
28
+ // @ts-expect-error
28
29
  withInvalidShapeType.shapes[0].type = 'invalid'
29
30
  expect(() => editor.putContentOntoCurrentPage(withInvalidShapeType)).toThrow()
30
31
  })
@@ -0,0 +1,67 @@
1
+ import { TLArrowShape, TLGeoShape, createShapeId } from '@tldraw/editor'
2
+ import { TestEditor, createDefaultShapes } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ const ids = {
7
+ box1: createShapeId('box1'),
8
+ }
9
+
10
+ beforeEach(() => {
11
+ editor = new TestEditor()
12
+ editor.createShapes(createDefaultShapes())
13
+ })
14
+
15
+ it('Uses typescript generics', () => {
16
+ expect(() => {
17
+ editor.updateShape({
18
+ id: ids.box1,
19
+ type: 'geo',
20
+ props: {
21
+ // @ts-expect-error
22
+ w: 'OH NO',
23
+ },
24
+ })
25
+
26
+ // Errors when updating a shape with unknown props
27
+ editor.updateShape({
28
+ id: ids.box1,
29
+ type: 'geo',
30
+ props: {
31
+ // @ts-expect-error
32
+ foo: 'bar',
33
+ },
34
+ })
35
+
36
+ // error here because we are giving the wrong props to the shape
37
+ editor.updateShape<TLGeoShape>({
38
+ id: ids.box1,
39
+ type: 'geo',
40
+ props: {
41
+ // @ts-expect-error
42
+ w: 'OH NO',
43
+ },
44
+ })
45
+
46
+ // Yep error here because we are giving the wrong generic
47
+ editor.updateShape<TLArrowShape>({
48
+ id: ids.box1,
49
+ //@ts-expect-error
50
+ type: 'geo',
51
+ //@ts-expect-error
52
+ props: { w: 'OH NO' },
53
+ })
54
+
55
+ // All good, correct match of generic and shape type
56
+ editor.updateShape<TLGeoShape>({
57
+ id: ids.box1,
58
+ type: 'geo',
59
+ props: { w: 100 },
60
+ })
61
+
62
+ editor.updateShape<TLGeoShape>({
63
+ id: ids.box1,
64
+ type: 'geo',
65
+ })
66
+ }).toThrow()
67
+ })
@@ -15,22 +15,38 @@ beforeEach(() => {
15
15
 
16
16
  it('Uses typescript generics', () => {
17
17
  expect(() => {
18
- // No error here because no generic, the editor doesn't know what this guy is
19
18
  editor.updateShapes([
20
19
  {
21
20
  id: ids.box1,
22
21
  type: 'geo',
23
- props: { w: 'OH NO' },
22
+ props: {
23
+ // @ts-expect-error
24
+ w: 'OH NO',
25
+ },
26
+ },
27
+ ])
28
+
29
+ // Errors when updating shapes with unknown props
30
+ editor.updateShapes([
31
+ {
32
+ id: ids.box1,
33
+ type: 'geo',
34
+ props: {
35
+ // @ts-expect-error
36
+ foo: 'bar',
37
+ },
24
38
  },
25
39
  ])
26
40
 
27
- // Yep error here because we are giving the wrong props to the shape
41
+ // error here because we are giving the wrong props to the shape
28
42
  editor.updateShapes<TLGeoShape>([
29
43
  {
30
44
  id: ids.box1,
31
45
  type: 'geo',
32
- //@ts-expect-error
33
- props: { w: 'OH NO' },
46
+ props: {
47
+ // @ts-expect-error
48
+ w: 'OH NO',
49
+ },
34
50
  },
35
51
  ])
36
52