@tldraw/editor 3.15.0 → 3.16.0-canary.03deb7f8fe34

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 (105) hide show
  1. package/dist-cjs/index.d.ts +181 -9
  2. package/dist-cjs/index.js +5 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +3 -1
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +4 -26
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
  11. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +1 -1
  12. package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
  13. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
  15. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
  17. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
  18. package/dist-cjs/lib/config/TLUserPreferences.js +8 -2
  19. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  20. package/dist-cjs/lib/editor/Editor.js +100 -58
  21. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -3
  23. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  25. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  26. package/dist-cjs/lib/exports/getSvgJsx.js +1 -2
  27. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  28. package/dist-cjs/lib/hooks/useCanvasEvents.js +22 -20
  29. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  30. package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
  31. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  32. package/dist-cjs/lib/hooks/useStateAttribute.js +35 -0
  33. package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
  34. package/dist-cjs/lib/license/Watermark.js +6 -6
  35. package/dist-cjs/lib/license/Watermark.js.map +1 -1
  36. package/dist-cjs/lib/options.js +1 -0
  37. package/dist-cjs/lib/options.js.map +2 -2
  38. package/dist-cjs/lib/utils/EditorAtom.js +45 -0
  39. package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
  40. package/dist-cjs/version.js +3 -3
  41. package/dist-cjs/version.js.map +1 -1
  42. package/dist-esm/index.d.mts +181 -9
  43. package/dist-esm/index.mjs +7 -1
  44. package/dist-esm/index.mjs.map +2 -2
  45. package/dist-esm/lib/TldrawEditor.mjs +3 -1
  46. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  47. package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
  48. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  49. package/dist-esm/lib/components/Shape.mjs +4 -26
  50. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  51. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
  52. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +1 -1
  53. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
  54. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  55. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
  56. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  57. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
  58. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
  59. package/dist-esm/lib/config/TLUserPreferences.mjs +8 -2
  60. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  61. package/dist-esm/lib/editor/Editor.mjs +100 -58
  62. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  63. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -3
  64. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  65. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  66. package/dist-esm/lib/exports/getSvgJsx.mjs +2 -2
  67. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  68. package/dist-esm/lib/hooks/useCanvasEvents.mjs +23 -21
  69. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  70. package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
  71. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  72. package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
  73. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
  74. package/dist-esm/lib/license/Watermark.mjs +6 -6
  75. package/dist-esm/lib/license/Watermark.mjs.map +1 -1
  76. package/dist-esm/lib/options.mjs +1 -0
  77. package/dist-esm/lib/options.mjs.map +2 -2
  78. package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
  79. package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
  80. package/dist-esm/version.mjs +3 -3
  81. package/dist-esm/version.mjs.map +1 -1
  82. package/editor.css +297 -311
  83. package/package.json +7 -7
  84. package/src/index.ts +7 -0
  85. package/src/lib/TldrawEditor.tsx +7 -5
  86. package/src/lib/components/MenuClickCapture.tsx +0 -8
  87. package/src/lib/components/Shape.tsx +6 -21
  88. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
  89. package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
  90. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +5 -1
  91. package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
  92. package/src/lib/config/TLUserPreferences.ts +7 -0
  93. package/src/lib/editor/Editor.ts +130 -81
  94. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +13 -0
  95. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -0
  96. package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
  97. package/src/lib/editor/types/misc-types.ts +73 -1
  98. package/src/lib/exports/getSvgJsx.tsx +2 -2
  99. package/src/lib/hooks/useCanvasEvents.ts +36 -32
  100. package/src/lib/hooks/useEditorComponents.tsx +7 -1
  101. package/src/lib/hooks/useStateAttribute.ts +15 -0
  102. package/src/lib/license/Watermark.tsx +6 -6
  103. package/src/lib/options.ts +2 -0
  104. package/src/lib/utils/EditorAtom.ts +37 -0
  105. package/src/version.ts +3 -3
@@ -2323,28 +2323,11 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
2323
2323
  { history: "ignore" }
2324
2324
  );
2325
2325
  const { currentScreenPoint, currentPagePoint } = this.inputs;
2326
- const { screenBounds } = this.store.unsafeGetWithoutCapture(import_tlschema.TLINSTANCE_ID);
2327
2326
  if (currentScreenPoint.x / z - x !== currentPagePoint.x || currentScreenPoint.y / z - y !== currentPagePoint.y) {
2328
- const event = {
2329
- type: "pointer",
2330
- target: "canvas",
2331
- name: "pointer_move",
2332
- // weird but true: we need to put the screen point back into client space
2333
- point: import_Vec.Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
2334
- pointerId: import_constants.INTERNAL_POINTER_IDS.CAMERA_MOVE,
2335
- ctrlKey: this.inputs.ctrlKey,
2336
- altKey: this.inputs.altKey,
2337
- shiftKey: this.inputs.shiftKey,
2338
- metaKey: this.inputs.metaKey,
2339
- accelKey: (0, import_keyboard.isAccelKey)(this.inputs),
2340
- button: 0,
2341
- isPen: this.getInstanceState().isPenMode ?? false
2342
- };
2343
- if (opts?.immediate) {
2344
- this._flushEventForTick(event);
2345
- } else {
2346
- this.dispatch(event);
2347
- }
2327
+ this.updatePointer({
2328
+ immediate: opts?.immediate,
2329
+ pointerId: import_constants.INTERNAL_POINTER_IDS.CAMERA_MOVE
2330
+ });
2348
2331
  }
2349
2332
  this._tickCameraState();
2350
2333
  });
@@ -3313,19 +3296,24 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3313
3296
  */
3314
3297
  deletePage(page) {
3315
3298
  const id = typeof page === "string" ? page : page.id;
3316
- this.run(() => {
3317
- if (this.getIsReadonly()) return;
3318
- const pages = this.getPages();
3319
- if (pages.length === 1) return;
3320
- const deletedPage = this.getPage(id);
3321
- if (!deletedPage) return;
3322
- if (id === this.getCurrentPageId()) {
3323
- const index = pages.findIndex((page2) => page2.id === id);
3324
- const next = pages[index - 1] ?? pages[index + 1];
3325
- this.setCurrentPage(next.id);
3326
- }
3327
- this.store.remove([deletedPage.id]);
3328
- });
3299
+ this.run(
3300
+ () => {
3301
+ if (this.getIsReadonly()) return;
3302
+ const pages = this.getPages();
3303
+ if (pages.length === 1) return;
3304
+ const deletedPage = this.getPage(id);
3305
+ if (!deletedPage) return;
3306
+ if (id === this.getCurrentPageId()) {
3307
+ const index = pages.findIndex((page2) => page2.id === id);
3308
+ const next = pages[index - 1] ?? pages[index + 1];
3309
+ this.setCurrentPage(next.id);
3310
+ }
3311
+ const shapes = this.getSortedChildIdsForParent(deletedPage.id);
3312
+ this.deleteShapes(shapes);
3313
+ this.store.remove([deletedPage.id]);
3314
+ },
3315
+ { ignoreShapeLock: true }
3316
+ );
3329
3317
  return this;
3330
3318
  }
3331
3319
  /**
@@ -3922,6 +3910,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3922
3910
  hitInside = false,
3923
3911
  hitFrameInside = false
3924
3912
  } = opts;
3913
+ const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin];
3925
3914
  let inHollowSmallestArea = Infinity;
3926
3915
  let inHollowSmallestAreaHit = null;
3927
3916
  let inMarginClosestToEdgeDistance = Infinity;
@@ -3931,7 +3920,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3931
3920
  return false;
3932
3921
  const pageMask = this.getShapeMask(shape);
3933
3922
  if (pageMask && !(0, import_utils2.pointInPolygon)(point, pageMask)) return false;
3934
- if (filter) return filter(shape);
3923
+ if (filter && !filter(shape)) return false;
3935
3924
  return true;
3936
3925
  });
3937
3926
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
@@ -3939,7 +3928,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3939
3928
  const geometry = this.getShapeGeometry(shape);
3940
3929
  const isGroup = geometry instanceof import_Group2d.Group2d;
3941
3930
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
3942
- if (this.isShapeOfType(shape, "frame") || this.isShapeOfType(shape, "arrow") && shape.props.text.trim() || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && this.getShapeUtil(shape).getText(shape)?.trim()) {
3931
+ if (this.isShapeOfType(shape, "frame") || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "arrow") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && this.getShapeUtil(shape).getText(shape)?.trim()) {
3943
3932
  for (const childGeometry of geometry.children) {
3944
3933
  if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
3945
3934
  return shape;
@@ -3947,8 +3936,8 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3947
3936
  }
3948
3937
  }
3949
3938
  if (this.isShapeOfType(shape, "frame")) {
3950
- const distance2 = geometry.distanceToPoint(pointInShapeSpace, hitInside);
3951
- if (Math.abs(distance2) <= margin) {
3939
+ const distance2 = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside);
3940
+ if (hitFrameInside ? distance2 > 0 && distance2 <= outerMargin || distance2 <= 0 && distance2 > -innerMargin : distance2 > 0 && distance2 <= outerMargin) {
3952
3941
  return inMarginClosestToEdgeHit || shape;
3953
3942
  }
3954
3943
  if (geometry.hitTestPoint(pointInShapeSpace, 0, true)) {
@@ -3968,10 +3957,10 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3968
3957
  }
3969
3958
  distance = minDistance;
3970
3959
  } else {
3971
- if (margin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
3960
+ if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
3972
3961
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside);
3973
3962
  } else {
3974
- if (geometry.bounds.containsPoint(pointInShapeSpace, margin)) {
3963
+ if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
3975
3964
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside);
3976
3965
  } else {
3977
3966
  distance = Infinity;
@@ -3979,12 +3968,22 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3979
3968
  }
3980
3969
  }
3981
3970
  if (geometry.isClosed) {
3982
- if (distance <= margin) {
3971
+ if (distance <= outerMargin || hitInside && distance <= 0 && distance > -innerMargin) {
3983
3972
  if (geometry.isFilled || isGroup && geometry.children[0].isFilled) {
3984
3973
  return inMarginClosestToEdgeHit || shape;
3985
3974
  } else {
3986
3975
  if (this.getShapePageBounds(shape).contains(viewportPageBounds)) continue;
3987
- if (Math.abs(distance) < margin) {
3976
+ if (hitInside ? (
3977
+ // On hitInside, the distance will be negative for hits inside
3978
+ // If the distance is positive, check against the outer margin
3979
+ distance > 0 && distance <= outerMargin || // If the distance is negative, check against the inner margin
3980
+ distance <= 0 && distance > -innerMargin
3981
+ ) : (
3982
+ // If hitInside is false, then sadly _we do not know_ whether the
3983
+ // point is inside or outside of the shape, so we check against
3984
+ // the max of the two margins
3985
+ Math.abs(distance) <= Math.max(innerMargin, outerMargin)
3986
+ )) {
3988
3987
  if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
3989
3988
  inMarginClosestToEdgeDistance = Math.abs(distance);
3990
3989
  inMarginClosestToEdgeHit = shape;
@@ -5503,8 +5502,7 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5503
5502
  const shapesMovingTogether = [shape];
5504
5503
  const boundsOfShapesMovingTogether = [shapePageBounds];
5505
5504
  if (!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
5506
- type: "stretch",
5507
- shapes: shapesToStretchFirstPass
5505
+ type: "stretch"
5508
5506
  })) {
5509
5507
  continue;
5510
5508
  }
@@ -5829,21 +5827,24 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
5829
5827
  }
5830
5828
  if (!partial.parentId || !(this.store.has(partial.parentId) || shapes.some((p) => p.id === partial.parentId))) {
5831
5829
  let parentId = this.getFocusedGroupId();
5832
- for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
5833
- const parent = currentPageShapesSorted[i];
5834
- const util = this.getShapeUtil(parent);
5835
- if (util.canReceiveNewChildrenOfType(parent, partial.type) && !this.isShapeHidden(parent) && this.isPointInShape(
5836
- parent,
5837
- // If no parent is provided, then we can treat the
5838
- // shape's provided x/y as being in the page's space.
5839
- { x: partial.x ?? 0, y: partial.y ?? 0 },
5840
- {
5841
- margin: 0,
5842
- hitInside: true
5830
+ const isPositioned = partial.x !== void 0 && partial.y !== void 0;
5831
+ if (isPositioned) {
5832
+ for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
5833
+ const parent = currentPageShapesSorted[i];
5834
+ const util = this.getShapeUtil(parent);
5835
+ if (util.canReceiveNewChildrenOfType(parent, partial.type) && !this.isShapeHidden(parent) && this.isPointInShape(
5836
+ parent,
5837
+ // If no parent is provided, then we can treat the
5838
+ // shape's provided x/y as being in the page's space.
5839
+ { x: partial.x ?? 0, y: partial.y ?? 0 },
5840
+ {
5841
+ margin: 0,
5842
+ hitInside: true
5843
+ }
5844
+ )) {
5845
+ parentId = parent.id;
5846
+ break;
5843
5847
  }
5844
- )) {
5845
- parentId = parent.id;
5846
- break;
5847
5848
  }
5848
5849
  }
5849
5850
  const prevParentId = partial.parentId;
@@ -7028,6 +7029,47 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
7028
7029
  this.dispatch({ type: "misc", name: "complete" });
7029
7030
  return this;
7030
7031
  }
7032
+ /**
7033
+ * Dispatch a pointer move event in the current position of the pointer. This is useful when
7034
+ * external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
7035
+ * the current interaction to respond to that change.
7036
+ *
7037
+ * @example
7038
+ * ```ts
7039
+ * editor.updatePointer()
7040
+ * ```
7041
+ *
7042
+ * @param options - The options for updating the pointer.
7043
+ * @returns The editor instance.
7044
+ * @public
7045
+ */
7046
+ updatePointer(options) {
7047
+ const event = {
7048
+ type: "pointer",
7049
+ target: "canvas",
7050
+ name: "pointer_move",
7051
+ point: options?.point ?? // weird but true: what `inputs` calls screen-space is actually viewport space. so
7052
+ // we need to convert back into true screen space first. we should fix this...
7053
+ import_Vec.Vec.Add(
7054
+ this.inputs.currentScreenPoint,
7055
+ this.store.unsafeGetWithoutCapture(import_tlschema.TLINSTANCE_ID).screenBounds
7056
+ ),
7057
+ pointerId: options?.pointerId ?? 0,
7058
+ button: options?.button ?? 0,
7059
+ isPen: options?.isPen ?? this.inputs.isPen,
7060
+ shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
7061
+ altKey: options?.altKey ?? this.inputs.altKey,
7062
+ ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
7063
+ metaKey: options?.metaKey ?? this.inputs.metaKey,
7064
+ accelKey: options?.accelKey ?? (0, import_keyboard.isAccelKey)(this.inputs)
7065
+ };
7066
+ if (options?.immediate) {
7067
+ this._flushEventForTick(event);
7068
+ } else {
7069
+ this.dispatch(event);
7070
+ }
7071
+ return this;
7072
+ }
7031
7073
  /**
7032
7074
  * Puts the editor into focused mode.
7033
7075
  *