@tldraw/editor 3.14.0-canary.ab6376ed9638 → 3.14.0-canary.ae54a012ca55

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 (97) hide show
  1. package/dist-cjs/index.d.ts +155 -55
  2. package/dist-cjs/index.js +4 -3
  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 +89 -29
  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/managers/FontManager/FontManager.js +1 -2
  10. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  11. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +3 -1
  12. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
  13. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +73 -42
  14. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  15. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +0 -10
  16. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  17. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  18. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +1 -1
  19. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +10 -6
  20. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +3 -3
  21. package/dist-cjs/lib/editor/tools/StateNode.js +3 -3
  22. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  23. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  24. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  25. package/dist-cjs/lib/hooks/useCanvasEvents.js +1 -2
  26. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  27. package/dist-cjs/lib/primitives/Box.js +0 -6
  28. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  29. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +6 -2
  30. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  31. package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
  32. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
  33. package/dist-cjs/lib/utils/reparenting.js +232 -0
  34. package/dist-cjs/lib/utils/reparenting.js.map +7 -0
  35. package/dist-cjs/lib/utils/richText.js +7 -2
  36. package/dist-cjs/lib/utils/richText.js.map +2 -2
  37. package/dist-cjs/version.js +3 -3
  38. package/dist-cjs/version.js.map +1 -1
  39. package/dist-esm/index.d.mts +155 -55
  40. package/dist-esm/index.mjs +4 -3
  41. package/dist-esm/index.mjs.map +2 -2
  42. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +1 -1
  43. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
  44. package/dist-esm/lib/editor/Editor.mjs +89 -29
  45. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  46. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  47. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +1 -2
  48. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  49. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +3 -1
  50. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
  51. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +73 -42
  52. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  53. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +0 -10
  54. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  55. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  56. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +1 -1
  57. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +10 -6
  58. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +3 -3
  59. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -3
  60. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  61. package/dist-esm/lib/hooks/useCanvasEvents.mjs +1 -2
  62. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  63. package/dist-esm/lib/primitives/Box.mjs +0 -6
  64. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  65. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +6 -2
  66. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  67. package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
  68. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
  69. package/dist-esm/lib/utils/reparenting.mjs +216 -0
  70. package/dist-esm/lib/utils/reparenting.mjs.map +7 -0
  71. package/dist-esm/lib/utils/richText.mjs +8 -3
  72. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  73. package/dist-esm/version.mjs +3 -3
  74. package/dist-esm/version.mjs.map +1 -1
  75. package/editor.css +442 -492
  76. package/package.json +8 -9
  77. package/src/index.ts +7 -1
  78. package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
  79. package/src/lib/editor/Editor.ts +105 -36
  80. package/src/lib/editor/bindings/BindingUtil.ts +6 -0
  81. package/src/lib/editor/managers/FontManager/FontManager.ts +1 -2
  82. package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +3 -1
  83. package/src/lib/editor/managers/TextManager/TextManager.test.ts +1 -5
  84. package/src/lib/editor/managers/TextManager/TextManager.ts +118 -86
  85. package/src/lib/editor/shapes/ShapeUtil.ts +47 -15
  86. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  87. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +22 -17
  88. package/src/lib/editor/tools/StateNode.ts +3 -3
  89. package/src/lib/editor/types/emit-types.ts +4 -0
  90. package/src/lib/editor/types/external-content.ts +11 -2
  91. package/src/lib/hooks/useCanvasEvents.ts +0 -1
  92. package/src/lib/primitives/Box.ts +0 -8
  93. package/src/lib/primitives/geometry/Geometry2d.ts +7 -2
  94. package/src/lib/utils/areShapesContentEqual.ts +1 -2
  95. package/src/lib/utils/reparenting.ts +383 -0
  96. package/src/lib/utils/richText.ts +9 -3
  97. package/src/version.ts +3 -3
@@ -357,6 +357,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
357
357
  __publicField(this, "externalContentHandlers", {
358
358
  text: null,
359
359
  files: null,
360
+ "file-replace": null,
360
361
  embed: null,
361
362
  "svg-text": null,
362
363
  url: null,
@@ -473,6 +474,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
473
474
  this.disposables.add(() => this.user.dispose());
474
475
  this.getContainer = getContainer;
475
476
  this.textMeasure = new TextManager(this);
477
+ this.disposables.add(() => this.textMeasure.dispose());
476
478
  this.fonts = new FontManager(this, fontAssetUrls);
477
479
  this._tickManager = new TickManager(this);
478
480
  class NewRoot extends RootState {
@@ -593,20 +595,21 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
593
595
  shape: {
594
596
  afterChange: (shapeBefore, shapeAfter) => {
595
597
  for (const binding of this.getBindingsInvolvingShape(shapeAfter)) {
596
- if (areShapesContentEqual(shapeBefore, shapeAfter)) continue;
597
598
  invalidBindingTypes.add(binding.type);
598
599
  if (binding.fromId === shapeAfter.id) {
599
600
  this.getBindingUtil(binding).onAfterChangeFromShape?.({
600
601
  binding,
601
602
  shapeBefore,
602
- shapeAfter
603
+ shapeAfter,
604
+ reason: "self"
603
605
  });
604
606
  }
605
607
  if (binding.toId === shapeAfter.id) {
606
608
  this.getBindingUtil(binding).onAfterChangeToShape?.({
607
609
  binding,
608
610
  shapeBefore,
609
- shapeAfter
611
+ shapeAfter,
612
+ reason: "self"
610
613
  });
611
614
  }
612
615
  }
@@ -620,14 +623,16 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
620
623
  this.getBindingUtil(binding).onAfterChangeFromShape?.({
621
624
  binding,
622
625
  shapeBefore: descendantShape,
623
- shapeAfter: descendantShape
626
+ shapeAfter: descendantShape,
627
+ reason: "ancestry"
624
628
  });
625
629
  }
626
630
  if (binding.toId === descendantShape.id) {
627
631
  this.getBindingUtil(binding).onAfterChangeToShape?.({
628
632
  binding,
629
633
  shapeBefore: descendantShape,
630
- shapeAfter: descendantShape
634
+ shapeAfter: descendantShape,
635
+ reason: "ancestry"
631
636
  });
632
637
  }
633
638
  }
@@ -1671,6 +1676,19 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
1671
1676
  getSelectionPageBounds() {
1672
1677
  return this.getShapesPageBounds(this.getSelectedShapeIds());
1673
1678
  }
1679
+ /**
1680
+ * The bounds of the selection bounding box in the current page space.
1681
+ *
1682
+ * @readonly
1683
+ * @public
1684
+ */
1685
+ getSelectionScreenBounds() {
1686
+ const bounds = this.getSelectionPageBounds();
1687
+ if (!bounds) return void 0;
1688
+ const { x, y } = this.pageToScreen(bounds.point);
1689
+ const zoom = this.getZoomLevel();
1690
+ return new Box(x, y, bounds.width * zoom, bounds.height * zoom);
1691
+ }
1674
1692
  /**
1675
1693
  * @internal
1676
1694
  */
@@ -4147,7 +4165,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4147
4165
  if (!id) return void 0;
4148
4166
  const freshShape = this.getShape(id);
4149
4167
  if (freshShape === void 0 || !isShapeId(freshShape.parentId)) return void 0;
4150
- return this.store.get(freshShape.parentId);
4168
+ return this.getShape(freshShape.parentId);
4151
4169
  }
4152
4170
  /**
4153
4171
  * If siblingShape and targetShape are siblings, this returns targetShape. If targetShape has an
@@ -4279,6 +4297,9 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4279
4297
  if (!pagePoint) continue;
4280
4298
  const newPoint = invertedParentTransform.applyToPoint(pagePoint);
4281
4299
  const newRotation = pageTransform.rotation() - parentPageRotation;
4300
+ if (shape.id === parentId) {
4301
+ throw Error("Attempted to reparent a shape to itself!");
4302
+ }
4282
4303
  changes.push({
4283
4304
  id: shape.id,
4284
4305
  type: shape.type,
@@ -4371,6 +4392,10 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4371
4392
  }
4372
4393
  return shapeIds;
4373
4394
  }
4395
+ /** @deprecated Use {@link Editor.getDraggingOverShape} instead */
4396
+ getDroppingOverShape(point, droppingShapes) {
4397
+ return this.getDraggingOverShape(point, droppingShapes);
4398
+ }
4374
4399
  /**
4375
4400
  * Get the shape that some shapes should be dropped on at a given point.
4376
4401
  *
@@ -4381,22 +4406,20 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4381
4406
  *
4382
4407
  * @public
4383
4408
  */
4384
- getDroppingOverShape(point, droppingShapes = []) {
4385
- const currentPageShapesSorted = this.getCurrentPageShapesSorted();
4386
- for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
4387
- const shape = currentPageShapesSorted[i];
4388
- if (
4389
- // ignore hidden shapes
4390
- this.isShapeHidden(shape) || // don't allow dropping on selected shapes
4391
- this.getSelectedShapeIds().includes(shape.id) || // only allow shapes that can receive children
4392
- !this.getShapeUtil(shape).canDropShapes(shape, droppingShapes) || // don't allow dropping a shape on itself or one of it's children
4393
- droppingShapes.find((s) => s.id === shape.id || this.hasAncestor(shape, s.id))
4394
- ) {
4395
- continue;
4396
- }
4397
- const maskedPageBounds = this.getShapeMaskedPageBounds(shape.id);
4398
- if (maskedPageBounds && maskedPageBounds.containsPoint(point) && this.getShapeGeometry(shape).hitTestPoint(this.getPointInShapeSpace(shape, point), 0, true)) {
4399
- return shape;
4409
+ getDraggingOverShape(point, droppingShapes) {
4410
+ const draggingShapes = compact(droppingShapes.map((s) => this.getShape(s))).filter(
4411
+ (s) => !s.isLocked && !this.isShapeHidden(s)
4412
+ );
4413
+ const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
4414
+ hitInside: true,
4415
+ margin: 0
4416
+ }).filter(
4417
+ (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !draggingShapes.includes(s)
4418
+ );
4419
+ for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
4420
+ const shapeUtil = this.getShapeUtil(maybeDraggingOverShape);
4421
+ if (shapeUtil.onDragShapesOver || shapeUtil.onDragShapesIn || shapeUtil.onDragShapesOut || shapeUtil.onDropShapesOver) {
4422
+ return maybeDraggingOverShape;
4400
4423
  }
4401
4424
  }
4402
4425
  }
@@ -4673,7 +4696,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4673
4696
  */
4674
4697
  duplicateShapes(shapes, offset) {
4675
4698
  this.run(() => {
4676
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4699
+ const _ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4700
+ const ids = this._shouldIgnoreShapeLock ? _ids : this._getUnlockedShapeIds(_ids);
4677
4701
  if (ids.length <= 0) return this;
4678
4702
  const initialIds = new Set(ids);
4679
4703
  const shapeIdSet = this.getShapeAndDescendantIds(ids);
@@ -4737,8 +4761,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4737
4761
  shape.index = index;
4738
4762
  });
4739
4763
  const shapesToCreate = shapesToCreateWithOriginals.map(({ shape }) => shape);
4740
- const maxShapesReached = shapesToCreate.length + this.getCurrentPageShapeIds().size > this.options.maxShapesPerPage;
4741
- if (maxShapesReached) {
4764
+ if (!this.canCreateShapes(shapesToCreate)) {
4742
4765
  alertMaxShapes(this);
4743
4766
  return;
4744
4767
  }
@@ -5753,6 +5776,26 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5753
5776
  getInitialMetaForShape(_shape) {
5754
5777
  return {};
5755
5778
  }
5779
+ /**
5780
+ * Get whether the provided shape can be created.
5781
+ *
5782
+ * @param shape - The shape or shape IDs to check.
5783
+ *
5784
+ * @public
5785
+ */
5786
+ canCreateShape(shape) {
5787
+ return this.canCreateShapes([shape]);
5788
+ }
5789
+ /**
5790
+ * Get whether the provided shapes can be created.
5791
+ *
5792
+ * @param shapes - The shapes or shape IDs to create.
5793
+ *
5794
+ * @public
5795
+ */
5796
+ canCreateShapes(shapes) {
5797
+ return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage;
5798
+ }
5756
5799
  /**
5757
5800
  * Create a single shape.
5758
5801
  *
@@ -5806,7 +5849,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5806
5849
  let parentId = this.getFocusedGroupId();
5807
5850
  for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
5808
5851
  const parent = currentPageShapesSorted[i];
5809
- if (!this.isShapeHidden(parent) && this.getShapeUtil(parent).canReceiveNewChildrenOfType(parent, partial.type) && this.isPointInShape(
5852
+ const util = this.getShapeUtil(parent);
5853
+ if (util.canReceiveNewChildrenOfType(parent, partial.type) && !this.isShapeHidden(parent) && this.isPointInShape(
5810
5854
  parent,
5811
5855
  // If no parent is provided, then we can treat the
5812
5856
  // shape's provided x/y as being in the page's space.
@@ -5881,6 +5925,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
5881
5925
  ...shape.meta
5882
5926
  };
5883
5927
  });
5928
+ this.emit("created-shapes", shapeRecordsToCreate);
5929
+ this.emit("edit");
5884
5930
  this.store.put(shapeRecordsToCreate);
5885
5931
  });
5886
5932
  return this;
@@ -6122,6 +6168,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
6122
6168
  updated = this.getShapeUtil(shape).onBeforeUpdate?.(shape, updated) ?? updated;
6123
6169
  updates.push(updated);
6124
6170
  }
6171
+ this.emit("edited-shapes", updates);
6172
+ this.emit("edit");
6125
6173
  this.store.put(updates);
6126
6174
  });
6127
6175
  }
@@ -6143,6 +6191,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
6143
6191
  allShapeIdsToDelete.add(childId);
6144
6192
  });
6145
6193
  }
6194
+ this.emit("deleted-shapes", [...allShapeIdsToDelete]);
6195
+ this.emit("edit");
6146
6196
  return this.run(() => this.store.remove([...allShapeIdsToDelete]));
6147
6197
  }
6148
6198
  deleteShape(_id) {
@@ -6486,6 +6536,14 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
6486
6536
  async putExternalContent(info) {
6487
6537
  return this.externalContentHandlers[info.type]?.(info);
6488
6538
  }
6539
+ /**
6540
+ * Handle replacing external content.
6541
+ *
6542
+ * @param info - Info about the external content.
6543
+ */
6544
+ async replaceExternalContent(info) {
6545
+ return this.externalContentHandlers[info.type]?.(info);
6546
+ }
6489
6547
  /**
6490
6548
  * Get content that can be exported for the given shape ids.
6491
6549
  *
@@ -6903,7 +6961,9 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
6903
6961
  previousScreenPoint,
6904
6962
  previousPagePoint,
6905
6963
  currentScreenPoint,
6906
- currentPagePoint
6964
+ currentPagePoint,
6965
+ originScreenPoint,
6966
+ originPagePoint
6907
6967
  } = this.inputs;
6908
6968
  const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID);
6909
6969
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera());
@@ -6921,8 +6981,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
6921
6981
  this.inputs.isPen = info.type === "pointer" && info.isPen;
6922
6982
  if (info.name === "pointer_down" || this.inputs.isPinching) {
6923
6983
  pointerVelocity.set(0, 0);
6924
- this.inputs.originScreenPoint.setTo(currentScreenPoint);
6925
- this.inputs.originPagePoint.setTo(currentPagePoint);
6984
+ originScreenPoint.setTo(currentScreenPoint);
6985
+ originPagePoint.setTo(currentPagePoint);
6926
6986
  }
6927
6987
  this.run(
6928
6988
  () => {