@tldraw/editor 3.14.0-canary.f8af44c4d1e2 → 3.14.0-canary.faba3f64c07f

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 (174) hide show
  1. package/dist-cjs/index.d.ts +150 -76
  2. package/dist-cjs/index.js +11 -10
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/config/TLSessionStateSnapshot.js +1 -12
  5. package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +3 -3
  6. package/dist-cjs/lib/editor/Editor.js +109 -94
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  9. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
  10. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  11. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
  12. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  13. package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
  14. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
  15. package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
  16. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
  17. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
  18. package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +1 -2
  19. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
  20. package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +64 -6
  21. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
  22. package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
  23. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
  24. package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +73 -42
  25. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
  26. package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
  27. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
  28. package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
  29. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
  30. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +0 -10
  31. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  33. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +1 -1
  34. package/dist-cjs/lib/editor/tools/StateNode.js +3 -3
  35. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  36. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  37. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  38. package/dist-cjs/lib/exports/getSvgJsx.js.map +1 -1
  39. package/dist-cjs/lib/hooks/useCanvasEvents.js +1 -2
  40. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  41. package/dist-cjs/lib/primitives/Box.js +0 -6
  42. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  43. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +6 -2
  44. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  45. package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
  46. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
  47. package/dist-cjs/lib/utils/reorderShapes.js +11 -10
  48. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  49. package/dist-cjs/lib/utils/reparenting.js +232 -0
  50. package/dist-cjs/lib/utils/reparenting.js.map +7 -0
  51. package/dist-cjs/lib/utils/richText.js +7 -2
  52. package/dist-cjs/lib/utils/richText.js.map +2 -2
  53. package/dist-cjs/version.js +3 -3
  54. package/dist-cjs/version.js.map +1 -1
  55. package/dist-esm/index.d.mts +150 -76
  56. package/dist-esm/index.mjs +15 -10
  57. package/dist-esm/index.mjs.map +2 -2
  58. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +1 -1
  59. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
  60. package/dist-esm/lib/editor/Editor.mjs +109 -94
  61. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  62. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  63. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
  64. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  65. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
  66. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  67. package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
  68. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
  69. package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
  70. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
  71. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
  72. package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +1 -2
  73. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
  74. package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +60 -2
  75. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
  76. package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
  77. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
  78. package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +73 -42
  79. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
  80. package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
  81. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
  82. package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
  83. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
  84. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +0 -10
  85. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  86. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  87. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +1 -1
  88. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -3
  89. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  90. package/dist-esm/lib/exports/getSvgJsx.mjs.map +1 -1
  91. package/dist-esm/lib/hooks/useCanvasEvents.mjs +1 -2
  92. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  93. package/dist-esm/lib/primitives/Box.mjs +0 -6
  94. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  95. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +6 -2
  96. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  97. package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
  98. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
  99. package/dist-esm/lib/utils/reorderShapes.mjs +11 -10
  100. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  101. package/dist-esm/lib/utils/reparenting.mjs +216 -0
  102. package/dist-esm/lib/utils/reparenting.mjs.map +7 -0
  103. package/dist-esm/lib/utils/richText.mjs +8 -3
  104. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  105. package/dist-esm/version.mjs +3 -3
  106. package/dist-esm/version.mjs.map +1 -1
  107. package/editor.css +442 -492
  108. package/package.json +8 -9
  109. package/src/index.ts +20 -8
  110. package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
  111. package/src/lib/editor/Editor.test.ts +252 -3
  112. package/src/lib/editor/Editor.ts +120 -101
  113. package/src/lib/editor/bindings/BindingUtil.ts +6 -0
  114. package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
  115. package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
  116. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
  117. package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
  118. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
  119. package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
  120. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
  121. package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
  122. package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
  123. package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +2 -3
  124. package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
  125. package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +73 -2
  126. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
  127. package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
  128. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
  129. package/src/lib/editor/managers/TextManager/TextManager.test.ts +407 -0
  130. package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +119 -87
  131. package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
  132. package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
  133. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
  134. package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
  135. package/src/lib/editor/shapes/ShapeUtil.ts +48 -16
  136. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  137. package/src/lib/editor/tools/StateNode.ts +3 -3
  138. package/src/lib/editor/types/emit-types.ts +4 -0
  139. package/src/lib/editor/types/external-content.ts +11 -2
  140. package/src/lib/exports/getSvgJsx.tsx +1 -1
  141. package/src/lib/hooks/useCanvasEvents.ts +0 -1
  142. package/src/lib/primitives/Box.ts +0 -8
  143. package/src/lib/primitives/geometry/Geometry2d.ts +7 -2
  144. package/src/lib/utils/areShapesContentEqual.ts +1 -2
  145. package/src/lib/utils/reorderShapes.ts +10 -13
  146. package/src/lib/utils/reparenting.ts +383 -0
  147. package/src/lib/utils/richText.ts +10 -4
  148. package/src/version.ts +3 -3
  149. package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
  150. package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
  151. package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
  152. package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
  153. package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
  154. package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
  155. package/dist-cjs/lib/editor/managers/Stack.js +0 -82
  156. package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
  157. package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
  158. package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
  159. package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
  160. package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
  161. package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
  162. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
  163. package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
  164. package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
  165. package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
  166. package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
  167. package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
  168. package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
  169. package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
  170. package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
  171. package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
  172. package/src/lib/editor/managers/Stack.ts +0 -71
  173. /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
  174. /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
@@ -148,16 +148,16 @@ import { bindingsIndex } from './derivations/bindingsIndex'
148
148
  import { notVisibleShapes } from './derivations/notVisibleShapes'
149
149
  import { parentsToChildren } from './derivations/parentsToChildren'
150
150
  import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
151
- import { ClickManager } from './managers/ClickManager'
152
- import { EdgeScrollManager } from './managers/EdgeScrollManager'
153
- import { FocusManager } from './managers/FocusManager'
154
- import { FontManager } from './managers/FontManager'
155
- import { HistoryManager } from './managers/HistoryManager'
156
- import { ScribbleManager } from './managers/ScribbleManager'
151
+ import { ClickManager } from './managers/ClickManager/ClickManager'
152
+ import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManager'
153
+ import { FocusManager } from './managers/FocusManager/FocusManager'
154
+ import { FontManager } from './managers/FontManager/FontManager'
155
+ import { HistoryManager } from './managers/HistoryManager/HistoryManager'
156
+ import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
157
157
  import { SnapManager } from './managers/SnapManager/SnapManager'
158
- import { TextManager } from './managers/TextManager'
159
- import { TickManager } from './managers/TickManager'
160
- import { UserPreferencesManager } from './managers/UserPreferencesManager'
158
+ import { TextManager } from './managers/TextManager/TextManager'
159
+ import { TickManager } from './managers/TickManager/TickManager'
160
+ import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
161
161
  import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
162
162
  import { RootState } from './tools/RootState'
163
163
  import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
@@ -328,7 +328,7 @@ export class Editor extends EventEmitter<TLEventMap> {
328
328
  this.store = store
329
329
  this.history = new HistoryManager<TLRecord>({
330
330
  store,
331
- annotateError: (error) => {
331
+ annotateError: (error: any) => {
332
332
  this.annotateError(error, { origin: 'history.batch', willCrashApp: true })
333
333
  this.crash(error)
334
334
  },
@@ -348,6 +348,8 @@ export class Editor extends EventEmitter<TLEventMap> {
348
348
  this.getContainer = getContainer
349
349
 
350
350
  this.textMeasure = new TextManager(this)
351
+ this.disposables.add(() => this.textMeasure.dispose())
352
+
351
353
  this.fonts = new FontManager(this, fontAssetUrls)
352
354
 
353
355
  this._tickManager = new TickManager(this)
@@ -506,14 +508,13 @@ export class Editor extends EventEmitter<TLEventMap> {
506
508
  shape: {
507
509
  afterChange: (shapeBefore, shapeAfter) => {
508
510
  for (const binding of this.getBindingsInvolvingShape(shapeAfter)) {
509
- if (areShapesContentEqual(shapeBefore, shapeAfter)) continue
510
-
511
511
  invalidBindingTypes.add(binding.type)
512
512
  if (binding.fromId === shapeAfter.id) {
513
513
  this.getBindingUtil(binding).onAfterChangeFromShape?.({
514
514
  binding,
515
515
  shapeBefore,
516
516
  shapeAfter,
517
+ reason: 'self',
517
518
  })
518
519
  }
519
520
  if (binding.toId === shapeAfter.id) {
@@ -521,6 +522,7 @@ export class Editor extends EventEmitter<TLEventMap> {
521
522
  binding,
522
523
  shapeBefore,
523
524
  shapeAfter,
525
+ reason: 'self',
524
526
  })
525
527
  }
526
528
  }
@@ -539,6 +541,7 @@ export class Editor extends EventEmitter<TLEventMap> {
539
541
  binding,
540
542
  shapeBefore: descendantShape,
541
543
  shapeAfter: descendantShape,
544
+ reason: 'ancestry',
542
545
  })
543
546
  }
544
547
  if (binding.toId === descendantShape.id) {
@@ -546,6 +549,7 @@ export class Editor extends EventEmitter<TLEventMap> {
546
549
  binding,
547
550
  shapeBefore: descendantShape,
548
551
  shapeAfter: descendantShape,
552
+ reason: 'ancestry',
549
553
  })
550
554
  }
551
555
  }
@@ -2118,6 +2122,20 @@ export class Editor extends EventEmitter<TLEventMap> {
2118
2122
  return this.getShapesPageBounds(this.getSelectedShapeIds())
2119
2123
  }
2120
2124
 
2125
+ /**
2126
+ * The bounds of the selection bounding box in the current page space.
2127
+ *
2128
+ * @readonly
2129
+ * @public
2130
+ */
2131
+ getSelectionScreenBounds(): Box | undefined {
2132
+ const bounds = this.getSelectionPageBounds()
2133
+ if (!bounds) return undefined
2134
+ const { x, y } = this.pageToScreen(bounds.point)
2135
+ const zoom = this.getZoomLevel()
2136
+ return new Box(x, y, bounds.width * zoom, bounds.height * zoom)
2137
+ }
2138
+
2121
2139
  /**
2122
2140
  * @internal
2123
2141
  */
@@ -4643,44 +4661,6 @@ export class Editor extends EventEmitter<TLEventMap> {
4643
4661
  )! as T
4644
4662
  }
4645
4663
 
4646
- private _shapePageGeometryCaches: Record<string, ComputedCache<Geometry2d, TLShape>> = {}
4647
-
4648
- /**
4649
- * Get the geometry of a shape in page-space.
4650
- *
4651
- * @example
4652
- * ```ts
4653
- * editor.getShapePageGeometry(myShape)
4654
- * editor.getShapePageGeometry(myShapeId)
4655
- * editor.getShapePageGeometry(myShapeId, { context: "arrow" })
4656
- * ```
4657
- *
4658
- * @param shape - The shape (or shape id) to get the geometry for.
4659
- * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
4660
- *
4661
- * @public
4662
- */
4663
- getShapePageGeometry<T extends Geometry2d>(shape: TLShape | TLShapeId, opts?: TLGeometryOpts): T {
4664
- const context = opts?.context ?? 'none'
4665
- if (!this._shapePageGeometryCaches[context]) {
4666
- this._shapePageGeometryCaches[context] = this.store.createComputedCache(
4667
- 'bounds',
4668
- (shape) => {
4669
- const geometry = this.getShapeGeometry(shape.id, opts)
4670
- const pageTransform = this.getShapePageTransform(shape.id)
4671
- return geometry.transform(pageTransform)
4672
- },
4673
- {
4674
- // we only depend directly on the shape id, and changing geometry/transform will update us anyway
4675
- areRecordsEqual: () => true,
4676
- }
4677
- )
4678
- }
4679
- return this._shapePageGeometryCaches[context].get(
4680
- typeof shape === 'string' ? shape : shape.id
4681
- )! as T
4682
- }
4683
-
4684
4664
  /** @internal */
4685
4665
  @computed private _getShapeHandlesCache(): ComputedCache<TLHandle[] | undefined, TLShape> {
4686
4666
  return this.store.createComputedCache(
@@ -4793,7 +4773,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4793
4773
  /** @internal */
4794
4774
  @computed private _getShapePageBoundsCache(): ComputedCache<Box, TLShape> {
4795
4775
  return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
4796
- return this.getShapePageGeometry(shape).bounds
4776
+ const pageTransform = this.getShapePageTransform(shape)
4777
+ if (!pageTransform) return undefined
4778
+ const geometry = this.getShapeGeometry(shape)
4779
+ return Box.FromPoints(pageTransform.applyToPoints(geometry.vertices))
4797
4780
  })
4798
4781
  }
4799
4782
 
@@ -4867,11 +4850,12 @@ export class Editor extends EventEmitter<TLEventMap> {
4867
4850
  if (frameAncestors.length === 0) return undefined
4868
4851
 
4869
4852
  const pageMask = frameAncestors
4870
- .map<Vec[] | undefined>(
4871
- (s) =>
4872
- // Apply the frame transform to the frame outline to get the frame outline in the current page space
4873
- this.getShapePageGeometry(s.id).vertices
4874
- )
4853
+ .map<Vec[] | undefined>((s) => {
4854
+ // Apply the frame transform to the frame outline to get the frame outline in the current page space
4855
+ const geometry = this.getShapeGeometry(s.id)
4856
+ const pageTransform = this.getShapePageTransform(s.id)
4857
+ return pageTransform.applyToPoints(geometry.vertices)
4858
+ })
4875
4859
  .reduce((acc, b) => {
4876
4860
  if (!(b && acc)) return undefined
4877
4861
  const intersection = intersectPolygonPolygon(acc, b)
@@ -5069,28 +5053,33 @@ export class Editor extends EventEmitter<TLEventMap> {
5069
5053
  *
5070
5054
  * @public
5071
5055
  */
5072
- isShapeOrAncestorLocked(shape?: TLShape): boolean
5073
- isShapeOrAncestorLocked(id?: TLShapeId): boolean
5074
- isShapeOrAncestorLocked(arg?: TLShape | TLShapeId): boolean {
5075
- const shape = typeof arg === 'string' ? this.getShape(arg) : arg
5076
- if (shape === undefined) return false
5077
- if (shape.isLocked) return true
5078
- return this.isShapeOrAncestorLocked(this.getShapeParent(shape))
5056
+ isShapeOrAncestorLocked(shape?: TLShape | TLShapeId): boolean {
5057
+ const _shape = shape && this.getShape(shape)
5058
+ if (_shape === undefined) return false
5059
+ if (_shape.isLocked) return true
5060
+ return this.isShapeOrAncestorLocked(this.getShapeParent(_shape))
5079
5061
  }
5080
5062
 
5063
+ /**
5064
+ * Get shapes that are outside of the viewport.
5065
+ *
5066
+ * @public
5067
+ */
5081
5068
  @computed
5082
- private _notVisibleShapes() {
5083
- return notVisibleShapes(this)
5069
+ getNotVisibleShapes() {
5070
+ return this._notVisibleShapes.get()
5084
5071
  }
5085
5072
 
5073
+ private _notVisibleShapes = notVisibleShapes(this)
5074
+
5086
5075
  /**
5087
- * Get culled shapes.
5076
+ * Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
5088
5077
  *
5089
5078
  * @public
5090
5079
  */
5091
5080
  @computed
5092
5081
  getCulledShapes() {
5093
- const notVisibleShapes = this._notVisibleShapes().get()
5082
+ const notVisibleShapes = this.getNotVisibleShapes()
5094
5083
  const selectedShapeIds = this.getSelectedShapeIds()
5095
5084
  const editingId = this.getEditingShapeId()
5096
5085
  const culledShapes = new Set<TLShapeId>(notVisibleShapes)
@@ -5339,21 +5328,23 @@ export class Editor extends EventEmitter<TLEventMap> {
5339
5328
  * @example
5340
5329
  * ```ts
5341
5330
  * editor.getShapesAtPoint({ x: 100, y: 100 })
5342
- * editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true, exact: true })
5331
+ * editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true, margin: 8 })
5343
5332
  * ```
5344
5333
  *
5345
5334
  * @param point - The page point to test.
5346
5335
  * @param opts - The options for the hit point testing.
5347
5336
  *
5337
+ * @returns An array of shapes at the given point, sorted in reverse order of their absolute z-index (top-most shape first).
5338
+ *
5348
5339
  * @public
5349
5340
  */
5350
5341
  getShapesAtPoint(
5351
5342
  point: VecLike,
5352
5343
  opts = {} as { margin?: number; hitInside?: boolean }
5353
5344
  ): TLShape[] {
5354
- return this.getCurrentPageShapes().filter(
5355
- (shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts)
5356
- )
5345
+ return this.getCurrentPageShapesSorted()
5346
+ .filter((shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts))
5347
+ .reverse()
5357
5348
  }
5358
5349
 
5359
5350
  /**
@@ -5537,7 +5528,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5537
5528
  if (!id) return undefined
5538
5529
  const freshShape = this.getShape(id)
5539
5530
  if (freshShape === undefined || !isShapeId(freshShape.parentId)) return undefined
5540
- return this.store.get(freshShape.parentId)
5531
+ return this.getShape(freshShape.parentId)
5541
5532
  }
5542
5533
 
5543
5534
  /**
@@ -5720,6 +5711,10 @@ export class Editor extends EventEmitter<TLEventMap> {
5720
5711
  const newPoint = invertedParentTransform.applyToPoint(pagePoint)
5721
5712
  const newRotation = pageTransform.rotation() - parentPageRotation
5722
5713
 
5714
+ if (shape.id === parentId) {
5715
+ throw Error('Attempted to reparent a shape to itself!')
5716
+ }
5717
+
5723
5718
  changes.push({
5724
5719
  id: shape.id,
5725
5720
  type: shape.type,
@@ -5823,6 +5818,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5823
5818
  return shapeIds
5824
5819
  }
5825
5820
 
5821
+ /** @deprecated Use {@link Editor.getDraggingOverShape} instead */
5822
+ getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
5823
+ return this.getDraggingOverShape(point, droppingShapes)
5824
+ }
5825
+
5826
5826
  /**
5827
5827
  * Get the shape that some shapes should be dropped on at a given point.
5828
5828
  *
@@ -5833,35 +5833,33 @@ export class Editor extends EventEmitter<TLEventMap> {
5833
5833
  *
5834
5834
  * @public
5835
5835
  */
5836
- getDroppingOverShape(point: VecLike, droppingShapes: TLShape[] = []) {
5837
- // starting from the top...
5838
- const currentPageShapesSorted = this.getCurrentPageShapesSorted()
5839
- for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
5840
- const shape = currentPageShapesSorted[i]
5841
-
5842
- if (
5843
- // ignore hidden shapes
5844
- this.isShapeHidden(shape) ||
5845
- // don't allow dropping on selected shapes
5846
- this.getSelectedShapeIds().includes(shape.id) ||
5847
- // only allow shapes that can receive children
5848
- !this.getShapeUtil(shape).canDropShapes(shape, droppingShapes) ||
5849
- // don't allow dropping a shape on itself or one of it's children
5850
- droppingShapes.find((s) => s.id === shape.id || this.hasAncestor(shape, s.id))
5851
- ) {
5852
- continue
5853
- }
5836
+ getDraggingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
5837
+ // get fresh moving shapes
5838
+ const draggingShapes = compact(droppingShapes.map((s) => this.getShape(s))).filter(
5839
+ (s) => !s.isLocked && !this.isShapeHidden(s)
5840
+ )
5854
5841
 
5855
- // Only allow dropping into the masked page bounds of the shape, e.g. when a frame is
5856
- // partially clipped by its own parent frame
5857
- const maskedPageBounds = this.getShapeMaskedPageBounds(shape.id)
5842
+ const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
5843
+ hitInside: true,
5844
+ margin: 0,
5845
+ }).filter(
5846
+ (s) =>
5847
+ !droppingShapes.includes(s) &&
5848
+ !s.isLocked &&
5849
+ !this.isShapeHidden(s) &&
5850
+ !draggingShapes.includes(s)
5851
+ )
5858
5852
 
5853
+ for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
5854
+ const shapeUtil = this.getShapeUtil(maybeDraggingOverShape)
5855
+ // Any shape that can handle any dragging interactions is a valid target
5859
5856
  if (
5860
- maskedPageBounds &&
5861
- maskedPageBounds.containsPoint(point) &&
5862
- this.getShapeGeometry(shape).hitTestPoint(this.getPointInShapeSpace(shape, point), 0, true)
5857
+ shapeUtil.onDragShapesOver ||
5858
+ shapeUtil.onDragShapesIn ||
5859
+ shapeUtil.onDragShapesOut ||
5860
+ shapeUtil.onDropShapesOver
5863
5861
  ) {
5864
- return shape
5862
+ return maybeDraggingOverShape
5865
5863
  }
5866
5864
  }
5867
5865
  }
@@ -6220,11 +6218,12 @@ export class Editor extends EventEmitter<TLEventMap> {
6220
6218
  */
6221
6219
  duplicateShapes(shapes: TLShapeId[] | TLShape[], offset?: VecLike): this {
6222
6220
  this.run(() => {
6223
- const ids =
6221
+ const _ids =
6224
6222
  typeof shapes[0] === 'string'
6225
6223
  ? (shapes as TLShapeId[])
6226
6224
  : (shapes as TLShape[]).map((s) => s.id)
6227
6225
 
6226
+ const ids = this._shouldIgnoreShapeLock ? _ids : this._getUnlockedShapeIds(_ids)
6228
6227
  if (ids.length <= 0) return this
6229
6228
 
6230
6229
  const initialIds = new Set(ids)
@@ -7814,9 +7813,10 @@ export class Editor extends EventEmitter<TLEventMap> {
7814
7813
 
7815
7814
  for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
7816
7815
  const parent = currentPageShapesSorted[i]
7816
+ const util = this.getShapeUtil(parent)
7817
7817
  if (
7818
+ util.canReceiveNewChildrenOfType(parent, partial.type) &&
7818
7819
  !this.isShapeHidden(parent) &&
7819
- this.getShapeUtil(parent).canReceiveNewChildrenOfType(parent, partial.type) &&
7820
7820
  this.isPointInShape(
7821
7821
  parent,
7822
7822
  // If no parent is provided, then we can treat the
@@ -7945,6 +7945,8 @@ export class Editor extends EventEmitter<TLEventMap> {
7945
7945
  }
7946
7946
  })
7947
7947
 
7948
+ this.emit('created-shapes', shapeRecordsToCreate)
7949
+ this.emit('edit')
7948
7950
  this.store.put(shapeRecordsToCreate)
7949
7951
  })
7950
7952
 
@@ -8339,6 +8341,8 @@ export class Editor extends EventEmitter<TLEventMap> {
8339
8341
  updates.push(updated)
8340
8342
  }
8341
8343
 
8344
+ this.emit('edited-shapes', updates)
8345
+ this.emit('edit')
8342
8346
  this.store.put(updates)
8343
8347
  })
8344
8348
  }
@@ -8388,6 +8392,8 @@ export class Editor extends EventEmitter<TLEventMap> {
8388
8392
  })
8389
8393
  }
8390
8394
 
8395
+ this.emit('deleted-shapes', [...allShapeIdsToDelete])
8396
+ this.emit('edit')
8391
8397
  return this.run(() => this.store.remove([...allShapeIdsToDelete]))
8392
8398
  }
8393
8399
 
@@ -8836,6 +8842,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8836
8842
  } = {
8837
8843
  text: null,
8838
8844
  files: null,
8845
+ 'file-replace': null,
8839
8846
  embed: null,
8840
8847
  'svg-text': null,
8841
8848
  url: null,
@@ -8885,6 +8892,15 @@ export class Editor extends EventEmitter<TLEventMap> {
8885
8892
  return this.externalContentHandlers[info.type]?.(info as any)
8886
8893
  }
8887
8894
 
8895
+ /**
8896
+ * Handle replacing external content.
8897
+ *
8898
+ * @param info - Info about the external content.
8899
+ */
8900
+ async replaceExternalContent<E>(info: TLExternalContent<E>): Promise<void> {
8901
+ return this.externalContentHandlers[info.type]?.(info as any)
8902
+ }
8903
+
8888
8904
  /**
8889
8905
  * Get content that can be exported for the given shape ids.
8890
8906
  *
@@ -9302,6 +9318,7 @@ export class Editor extends EventEmitter<TLEventMap> {
9302
9318
  if (rootShapes.length === 1) {
9303
9319
  const onlyRoot = rootShapes[0] as TLFrameShape
9304
9320
  // If the old bounds are in the viewport...
9321
+ // todo: replace frame references with shapes that can accept children
9305
9322
  if (this.isShapeOfType<TLFrameShape>(onlyRoot, 'frame')) {
9306
9323
  while (
9307
9324
  this.getShapesAtPoint(point).some(
@@ -9503,6 +9520,8 @@ export class Editor extends EventEmitter<TLEventMap> {
9503
9520
  previousPagePoint,
9504
9521
  currentScreenPoint,
9505
9522
  currentPagePoint,
9523
+ originScreenPoint,
9524
+ originPagePoint,
9506
9525
  } = this.inputs
9507
9526
 
9508
9527
  const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
@@ -9531,8 +9550,8 @@ export class Editor extends EventEmitter<TLEventMap> {
9531
9550
  // Reset velocity on pointer down, or when a pinch starts or ends
9532
9551
  if (info.name === 'pointer_down' || this.inputs.isPinching) {
9533
9552
  pointerVelocity.set(0, 0)
9534
- this.inputs.originScreenPoint.setTo(currentScreenPoint)
9535
- this.inputs.originPagePoint.setTo(currentPagePoint)
9553
+ originScreenPoint.setTo(currentScreenPoint)
9554
+ originPagePoint.setTo(currentPagePoint)
9536
9555
  }
9537
9556
 
9538
9557
  // todo: We only have to do this if there are multiple users in the document
@@ -62,6 +62,12 @@ export interface BindingOnShapeChangeOptions<Binding extends TLUnknownBinding> {
62
62
  shapeBefore: TLShape
63
63
  /** The shape record after the change is made. */
64
64
  shapeAfter: TLShape
65
+ /**
66
+ * Why did this shape change?
67
+ * - 'self': the shape itself changed
68
+ * - 'ancestry': the ancestry of the shape changed, but the shape itself may not have done
69
+ */
70
+ reason: 'self' | 'ancestry'
65
71
  }
66
72
 
67
73
  /**
@@ -1,41 +1,42 @@
1
1
  import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'
2
- import { TLBinding, TLShapeId } from '@tldraw/tlschema'
2
+ import { TLArrowBinding, TLBinding, TLShapeId, TLUnknownBinding } from '@tldraw/tlschema'
3
3
  import { objectMapValues } from '@tldraw/utils'
4
4
  import { Editor } from '../Editor'
5
5
 
6
6
  type TLBindingsIndex = Map<TLShapeId, TLBinding[]>
7
7
 
8
- export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
9
- const { store } = editor
10
- const bindingsHistory = store.query.filterHistory('binding')
11
- const bindingsQuery = store.query.records('binding')
12
- function fromScratch() {
13
- const allBindings = bindingsQuery.get() as TLBinding[]
8
+ function fromScratch(bindingsQuery: Computed<(TLArrowBinding | TLUnknownBinding)[], unknown>) {
9
+ const allBindings = bindingsQuery.get() as TLBinding[]
14
10
 
15
- const shape2Binding: TLBindingsIndex = new Map()
11
+ const shapesToBindings: TLBindingsIndex = new Map()
16
12
 
17
- for (const binding of allBindings) {
18
- const { fromId, toId } = binding
19
- const bindingsForFromShape = shape2Binding.get(fromId)
20
- if (!bindingsForFromShape) {
21
- shape2Binding.set(fromId, [binding])
22
- } else {
23
- bindingsForFromShape.push(binding)
24
- }
25
- const bindingsForToShape = shape2Binding.get(toId)
26
- if (!bindingsForToShape) {
27
- shape2Binding.set(toId, [binding])
28
- } else {
29
- bindingsForToShape.push(binding)
30
- }
13
+ for (const binding of allBindings) {
14
+ const { fromId, toId } = binding
15
+ const bindingsForFromShape = shapesToBindings.get(fromId)
16
+ if (!bindingsForFromShape) {
17
+ shapesToBindings.set(fromId, [binding])
18
+ } else {
19
+ bindingsForFromShape.push(binding)
20
+ }
21
+ const bindingsForToShape = shapesToBindings.get(toId)
22
+ if (!bindingsForToShape) {
23
+ shapesToBindings.set(toId, [binding])
24
+ } else {
25
+ bindingsForToShape.push(binding)
31
26
  }
32
-
33
- return shape2Binding
34
27
  }
35
28
 
29
+ return shapesToBindings
30
+ }
31
+
32
+ export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
33
+ const { store } = editor
34
+ const bindingsHistory = store.query.filterHistory('binding')
35
+ const bindingsQuery = store.query.records('binding')
36
+
36
37
  return computed<TLBindingsIndex>('arrowBindingsIndex', (_lastValue, lastComputedEpoch) => {
37
38
  if (isUninitialized(_lastValue)) {
38
- return fromScratch()
39
+ return fromScratch(bindingsQuery)
39
40
  }
40
41
 
41
42
  const lastValue = _lastValue
@@ -43,7 +44,7 @@ export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
43
44
  const diff = bindingsHistory.getDiffSince(lastComputedEpoch)
44
45
 
45
46
  if (diff === RESET_VALUE) {
46
- return fromScratch()
47
+ return fromScratch(bindingsQuery)
47
48
  }
48
49
 
49
50
  let nextValue: TLBindingsIndex | undefined = undefined
@@ -1,45 +1,48 @@
1
- import { computed, isUninitialized, RESET_VALUE } from '@tldraw/state'
2
- import { RecordsDiff } from '@tldraw/store'
1
+ import { Computed, computed, isUninitialized, RESET_VALUE } from '@tldraw/state'
2
+ import { CollectionDiff, RecordsDiff } from '@tldraw/store'
3
3
  import { isShape, TLParentId, TLRecord, TLShape, TLShapeId, TLStore } from '@tldraw/tlschema'
4
4
  import { compact, sortByIndex } from '@tldraw/utils'
5
5
 
6
- type Parents2Children = Record<TLParentId, TLShapeId[]>
6
+ type ParentShapeIdsToChildShapeIds = Record<TLParentId, TLShapeId[]>
7
7
 
8
- export const parentsToChildren = (store: TLStore) => {
9
- const shapeIdsQuery = store.query.ids<'shape'>('shape')
10
- const shapeHistory = store.query.filterHistory('shape')
8
+ function fromScratch(
9
+ shapeIdsQuery: Computed<Set<TLShapeId>, CollectionDiff<TLShapeId>>,
10
+ store: TLStore
11
+ ) {
12
+ const result: ParentShapeIdsToChildShapeIds = {}
13
+ const shapeIds = shapeIdsQuery.get()
14
+ const shapes = Array(shapeIds.size) as TLShape[]
15
+ shapeIds.forEach((id) => shapes.push(store.get(id)!))
11
16
 
12
- function fromScratch() {
13
- const result: Parents2Children = {}
14
- const shapeIds = shapeIdsQuery.get()
15
- const shapes = Array(shapeIds.size) as TLShape[]
16
- shapeIds.forEach((id) => shapes.push(store.get(id)!))
17
+ // Sort the shapes by index
18
+ shapes.sort(sortByIndex)
17
19
 
18
- // Sort the shapes by index
19
- shapes.sort(sortByIndex)
20
+ // Populate the result object with an array for each parent.
21
+ shapes.forEach((shape) => {
22
+ if (!result[shape.parentId]) {
23
+ result[shape.parentId] = []
24
+ }
25
+ result[shape.parentId].push(shape.id)
26
+ })
20
27
 
21
- // Populate the result object with an array for each parent.
22
- shapes.forEach((shape) => {
23
- if (!result[shape.parentId]) {
24
- result[shape.parentId] = []
25
- }
26
- result[shape.parentId].push(shape.id)
27
- })
28
+ return result
29
+ }
28
30
 
29
- return result
30
- }
31
+ export const parentsToChildren = (store: TLStore) => {
32
+ const shapeIdsQuery = store.query.ids<'shape'>('shape')
33
+ const shapeHistory = store.query.filterHistory('shape')
31
34
 
32
- return computed<Parents2Children>(
35
+ return computed<ParentShapeIdsToChildShapeIds>(
33
36
  'parentsToChildrenWithIndexes',
34
37
  (lastValue, lastComputedEpoch) => {
35
38
  if (isUninitialized(lastValue)) {
36
- return fromScratch()
39
+ return fromScratch(shapeIdsQuery, store)
37
40
  }
38
41
 
39
42
  const diff = shapeHistory.getDiffSince(lastComputedEpoch)
40
43
 
41
44
  if (diff === RESET_VALUE) {
42
- return fromScratch()
45
+ return fromScratch(shapeIdsQuery, store)
43
46
  }
44
47
 
45
48
  if (diff.length === 0) return lastValue