@tldraw/editor 3.12.1 → 3.13.0-canary.064d79cae9fb

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 (94) hide show
  1. package/CHANGELOG.md +0 -20
  2. package/dist-cjs/index.d.ts +30 -14
  3. package/dist-cjs/index.js +1 -1
  4. package/dist-cjs/lib/TldrawEditor.js +2 -1
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/Shape.js +12 -8
  7. package/dist-cjs/lib/components/Shape.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +27 -2
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +14 -12
  11. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +17 -11
  13. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultSpinner.js +1 -1
  15. package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +2 -2
  16. package/dist-cjs/lib/editor/Editor.js +46 -28
  17. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  18. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  19. package/dist-cjs/lib/exports/getSvgJsx.js +12 -3
  20. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  21. package/dist-cjs/lib/hooks/useDocumentEvents.js +3 -2
  22. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  23. package/dist-cjs/lib/hooks/useEditorComponents.js +16 -15
  24. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  25. package/dist-cjs/lib/license/LicenseManager.js +8 -1
  26. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  27. package/dist-cjs/lib/options.js.map +2 -2
  28. package/dist-cjs/lib/utils/areShapesContentEqual.js +25 -0
  29. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +7 -0
  30. package/dist-cjs/lib/utils/dom.js +3 -3
  31. package/dist-cjs/lib/utils/dom.js.map +2 -2
  32. package/dist-cjs/lib/utils/nearestMultiple.js +34 -0
  33. package/dist-cjs/lib/utils/nearestMultiple.js.map +7 -0
  34. package/dist-cjs/lib/utils/rotation.js +5 -5
  35. package/dist-cjs/lib/utils/rotation.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 +30 -14
  39. package/dist-esm/index.mjs +1 -1
  40. package/dist-esm/lib/TldrawEditor.mjs +2 -1
  41. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  42. package/dist-esm/lib/components/Shape.mjs +12 -8
  43. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  44. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +27 -2
  45. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  46. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +14 -12
  47. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  48. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +17 -11
  49. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  50. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +1 -1
  51. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
  52. package/dist-esm/lib/editor/Editor.mjs +46 -28
  53. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  54. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  55. package/dist-esm/lib/exports/getSvgJsx.mjs +12 -3
  56. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  57. package/dist-esm/lib/hooks/useDocumentEvents.mjs +3 -2
  58. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  59. package/dist-esm/lib/hooks/useEditorComponents.mjs +16 -15
  60. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  61. package/dist-esm/lib/license/LicenseManager.mjs +8 -1
  62. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  63. package/dist-esm/lib/options.mjs.map +2 -2
  64. package/dist-esm/lib/utils/areShapesContentEqual.mjs +5 -0
  65. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +7 -0
  66. package/dist-esm/lib/utils/dom.mjs +3 -3
  67. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  68. package/dist-esm/lib/utils/nearestMultiple.mjs +14 -0
  69. package/dist-esm/lib/utils/nearestMultiple.mjs.map +7 -0
  70. package/dist-esm/lib/utils/rotation.mjs +5 -5
  71. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  72. package/dist-esm/version.mjs +3 -3
  73. package/dist-esm/version.mjs.map +1 -1
  74. package/editor.css +11 -0
  75. package/package.json +7 -7
  76. package/src/lib/TldrawEditor.tsx +6 -1
  77. package/src/lib/components/Shape.tsx +14 -10
  78. package/src/lib/components/default-components/DefaultCanvas.tsx +32 -2
  79. package/src/lib/components/default-components/DefaultErrorFallback.tsx +25 -14
  80. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +17 -8
  81. package/src/lib/components/default-components/DefaultSpinner.tsx +1 -1
  82. package/src/lib/editor/Editor.ts +43 -27
  83. package/src/lib/editor/shapes/ShapeUtil.ts +13 -1
  84. package/src/lib/exports/getSvgJsx.tsx +16 -7
  85. package/src/lib/hooks/useDocumentEvents.ts +7 -2
  86. package/src/lib/hooks/useEditorComponents.tsx +32 -28
  87. package/src/lib/license/LicenseManager.test.ts +40 -0
  88. package/src/lib/license/LicenseManager.ts +13 -1
  89. package/src/lib/options.ts +4 -0
  90. package/src/lib/utils/areShapesContentEqual.ts +4 -0
  91. package/src/lib/utils/dom.ts +4 -4
  92. package/src/lib/utils/nearestMultiple.ts +13 -0
  93. package/src/lib/utils/rotation.ts +8 -6
  94. package/src/version.ts +3 -3
@@ -132,6 +132,7 @@ import { Group2d } from "../primitives/geometry/Group2d.mjs";
132
132
  import { intersectPolygonPolygon } from "../primitives/intersect.mjs";
133
133
  import { PI, approximately, areAnglesCompatible, clamp, pointInPolygon } from "../primitives/utils.mjs";
134
134
  import { SharedStyleMap } from "../utils/SharedStylesMap.mjs";
135
+ import { areShapesContentEqual } from "../utils/areShapesContentEqual.mjs";
135
136
  import { dataUrlToFile } from "../utils/assets.mjs";
136
137
  import { debugFlags } from "../utils/debug-flags.mjs";
137
138
  import {
@@ -1381,8 +1382,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
1381
1382
  return this.getCurrentPageState().selectedShapeIds;
1382
1383
  }
1383
1384
  getSelectedShapes() {
1384
- const { selectedShapeIds } = this.getCurrentPageState();
1385
- return compact(selectedShapeIds.map((id) => this.store.get(id)));
1385
+ return compact(this.getSelectedShapeIds().map((id) => this.store.get(id)));
1386
1386
  }
1387
1387
  /**
1388
1388
  * Select one or more shapes.
@@ -1984,12 +1984,22 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
1984
1984
  }
1985
1985
  return baseCamera;
1986
1986
  }
1987
+ _getFollowingPresence(targetUserId) {
1988
+ const visited = [this.user.getId()];
1989
+ const collaborators = this.getCollaborators();
1990
+ let leaderPresence = null;
1991
+ while (targetUserId && !visited.includes(targetUserId)) {
1992
+ leaderPresence = collaborators.find((c) => c.userId === targetUserId) ?? null;
1993
+ targetUserId = leaderPresence?.followingUserId ?? null;
1994
+ if (leaderPresence) {
1995
+ visited.push(leaderPresence.userId);
1996
+ }
1997
+ }
1998
+ return leaderPresence;
1999
+ }
1987
2000
  getViewportPageBoundsForFollowing() {
1988
- const followingUserId = this.getInstanceState().followingUserId;
1989
- if (!followingUserId) return null;
1990
- const leaderPresence = this.getCollaborators().find((c) => c.userId === followingUserId);
1991
- if (!leaderPresence) return null;
1992
- if (!leaderPresence.camera || !leaderPresence.screenBounds) return null;
2001
+ const leaderPresence = this._getFollowingPresence(this.getInstanceState().followingUserId);
2002
+ if (!leaderPresence?.camera || !leaderPresence?.screenBounds) return null;
1993
2003
  const { w: lw, h: lh } = leaderPresence.screenBounds;
1994
2004
  const { x: lx, y: ly, z: lz } = leaderPresence.camera;
1995
2005
  const theirViewport = new Box(-lx, -ly, lw / lz, lh / lz);
@@ -2896,34 +2906,30 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
2896
2906
  */
2897
2907
  startFollowingUser(userId) {
2898
2908
  this.stopFollowingUser();
2899
- const leaderPresences = this._getCollaboratorsQuery().get().filter((p) => p.userId === userId);
2900
- if (!leaderPresences.length) {
2901
- console.warn("User not found");
2902
- return this;
2903
- }
2904
2909
  const thisUserId = this.user.getId();
2905
2910
  if (!thisUserId) {
2906
2911
  console.warn("You should set the userId for the current instance before following a user");
2907
2912
  }
2908
- if (leaderPresences.some((p) => p.followingUserId === thisUserId)) {
2913
+ const leaderPresence = this._getFollowingPresence(userId);
2914
+ if (!leaderPresence) {
2909
2915
  return this;
2910
2916
  }
2911
2917
  const latestLeaderPresence = computed("latestLeaderPresence", () => {
2912
- return this.getCollaborators().find((p) => p.userId === userId);
2918
+ return this._getFollowingPresence(userId);
2913
2919
  });
2914
2920
  transact(() => {
2915
2921
  this.updateInstanceState({ followingUserId: userId }, { history: "ignore" });
2916
2922
  const dispose = react("update current page", () => {
2917
- const leaderPresence = latestLeaderPresence.get();
2918
- if (!leaderPresence) {
2923
+ const leaderPresence2 = latestLeaderPresence.get();
2924
+ if (!leaderPresence2) {
2919
2925
  this.stopFollowingUser();
2920
2926
  return;
2921
2927
  }
2922
- if (leaderPresence.currentPageId !== this.getCurrentPageId() && this.getPage(leaderPresence.currentPageId)) {
2928
+ if (leaderPresence2.currentPageId !== this.getCurrentPageId() && this.getPage(leaderPresence2.currentPageId)) {
2923
2929
  this.run(
2924
2930
  () => {
2925
2931
  this.store.put([
2926
- { ...this.getInstanceState(), currentPageId: leaderPresence.currentPageId }
2932
+ { ...this.getInstanceState(), currentPageId: leaderPresence2.currentPageId }
2927
2933
  ]);
2928
2934
  this._isLockedOnFollowingUser.set(true);
2929
2935
  },
@@ -2938,8 +2944,8 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
2938
2944
  this.off("stop-following", cancel);
2939
2945
  };
2940
2946
  const moveTowardsUser = () => {
2941
- const leaderPresence = latestLeaderPresence.get();
2942
- if (!leaderPresence) {
2947
+ const leaderPresence2 = latestLeaderPresence.get();
2948
+ if (!leaderPresence2) {
2943
2949
  this.stopFollowingUser();
2944
2950
  return;
2945
2951
  }
@@ -3468,7 +3474,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
3468
3474
  this.fonts.trackFontsForShape(shape2);
3469
3475
  return this.getShapeUtil(shape2).getGeometry(shape2, opts);
3470
3476
  },
3471
- { areRecordsEqual: (a, b) => a.props === b.props }
3477
+ { areRecordsEqual: areShapesContentEqual }
3472
3478
  );
3473
3479
  }
3474
3480
  return this._shapeGeometryCaches[context].get(
@@ -3511,9 +3517,15 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
3511
3517
  );
3512
3518
  }
3513
3519
  _getShapeHandlesCache() {
3514
- return this.store.createComputedCache("handles", (shape) => {
3515
- return this.getShapeUtil(shape).getHandles?.(shape);
3516
- });
3520
+ return this.store.createComputedCache(
3521
+ "handles",
3522
+ (shape) => {
3523
+ return this.getShapeUtil(shape).getHandles?.(shape);
3524
+ },
3525
+ {
3526
+ areRecordsEqual: areShapesContentEqual
3527
+ }
3528
+ );
3517
3529
  }
3518
3530
  /**
3519
3531
  * Get the handles (if any) for a shape.
@@ -4397,9 +4409,15 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
4397
4409
  }
4398
4410
  _getBindingsIndexCache() {
4399
4411
  const index = bindingsIndex(this);
4400
- return this.store.createComputedCache("bindingsIndex", (shape) => {
4401
- return index.get().get(shape.id);
4402
- });
4412
+ return this.store.createComputedCache(
4413
+ "bindingsIndex",
4414
+ (shape) => {
4415
+ return index.get().get(shape.id);
4416
+ },
4417
+ // we can ignore the shape equality check here because the index is
4418
+ // computed incrementally based on what bindings are in the store
4419
+ { areRecordsEqual: () => true }
4420
+ );
4403
4421
  }
4404
4422
  /**
4405
4423
  * Get a binding from the store by its ID if it exists.
@@ -7474,7 +7492,7 @@ class Editor extends (_a = EventEmitter, _getIsShapeHiddenCache_dec = [computed]
7474
7492
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera());
7475
7493
  const { x: dx, y: dy, z: dz = 0 } = info.delta;
7476
7494
  let behavior = wheelBehavior;
7477
- if (inputs.ctrlKey) behavior = wheelBehavior === "pan" ? "zoom" : "pan";
7495
+ if (info.ctrlKey) behavior = wheelBehavior === "pan" ? "zoom" : "pan";
7478
7496
  switch (behavior) {
7479
7497
  case "zoom": {
7480
7498
  const { x, y } = this.inputs.currentScreenPoint;