@tldraw/editor 5.3.2 → 5.4.0-canary.0063438d1c75

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 (55) hide show
  1. package/dist-cjs/index.d.ts +94 -52
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +86 -26
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +7 -3
  7. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
  8. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +6 -3
  9. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
  11. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  12. package/dist-cjs/lib/globals/editors.js +57 -0
  13. package/dist-cjs/lib/globals/editors.js.map +7 -0
  14. package/dist-cjs/lib/hooks/useViewportHeight.js.map +2 -2
  15. package/dist-cjs/lib/primitives/geometry/Group2d.js +63 -21
  16. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  17. package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
  18. package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
  19. package/dist-cjs/version.js +3 -3
  20. package/dist-cjs/version.js.map +1 -1
  21. package/dist-esm/index.d.mts +94 -52
  22. package/dist-esm/index.mjs +3 -1
  23. package/dist-esm/index.mjs.map +2 -2
  24. package/dist-esm/lib/editor/Editor.mjs +86 -26
  25. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  26. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
  27. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  28. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -4
  29. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  30. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  31. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  32. package/dist-esm/lib/globals/editors.mjs +37 -0
  33. package/dist-esm/lib/globals/editors.mjs.map +7 -0
  34. package/dist-esm/lib/hooks/useViewportHeight.mjs.map +2 -2
  35. package/dist-esm/lib/primitives/geometry/Group2d.mjs +63 -21
  36. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  37. package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
  38. package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
  39. package/dist-esm/version.mjs +3 -3
  40. package/dist-esm/version.mjs.map +1 -1
  41. package/package.json +7 -7
  42. package/src/index.ts +2 -0
  43. package/src/lib/editor/Editor.ts +126 -37
  44. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
  45. package/src/lib/editor/managers/TextManager/TextManager.test.ts +42 -0
  46. package/src/lib/editor/managers/TextManager/TextManager.ts +10 -8
  47. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  48. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  49. package/src/lib/globals/editors.test.ts +76 -0
  50. package/src/lib/globals/editors.ts +53 -0
  51. package/src/lib/hooks/useViewportHeight.ts +9 -1
  52. package/src/lib/primitives/geometry/Group2d.ts +63 -27
  53. package/src/lib/utils/normalizeWheel.test.ts +39 -0
  54. package/src/lib/utils/normalizeWheel.ts +6 -0
  55. package/src/version.ts +3 -3
@@ -83,6 +83,7 @@ import {
83
83
  import { getOwnerWindow } from "../exports/domUtils.mjs";
84
84
  import { exportToSvg } from "../exports/exportToSvg.mjs";
85
85
  import { getSvgAsImageWithOptions, trimSvgToContent } from "../exports/getSvgAsImage.mjs";
86
+ import { registerMountedEditor, unregisterMountedEditor } from "../globals/editors.mjs";
86
87
  import { tlmenus } from "../globals/menus.mjs";
87
88
  import { tltime } from "../globals/time.mjs";
88
89
  import { defaultTldrawOptions } from "../options.mjs";
@@ -604,9 +605,11 @@ class Editor extends EventEmitter {
604
605
  this.on("tick", this._flushEventsForTick);
605
606
  this.on("mount", () => {
606
607
  this._isMounted.set(true);
608
+ registerMountedEditor(this);
607
609
  });
608
610
  this.on("unmount", () => {
609
611
  this._isMounted.set(false);
612
+ unregisterMountedEditor(this);
610
613
  });
611
614
  this.timers.requestAnimationFrame(() => {
612
615
  this._tickManager.start();
@@ -848,6 +851,7 @@ class Editor extends EventEmitter {
848
851
  if (this._isMounted.get()) {
849
852
  this.emit("unmount");
850
853
  }
854
+ unregisterMountedEditor(this);
851
855
  this.stopCameraAnimation();
852
856
  if (this.getInstanceState().followingUserId) {
853
857
  this.stopFollowingUser();
@@ -4339,17 +4343,16 @@ class Editor extends EventEmitter {
4339
4343
  let inMarginClosestToEdgeHit = null;
4340
4344
  const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin());
4341
4345
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin);
4342
- const shapesToCheck = (opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted()).filter((shape) => {
4343
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4344
- if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group"))
4345
- return false;
4346
- const pageMask = this.getShapeMask(shape);
4347
- if (pageMask && !pointInPolygon(point, pageMask)) return false;
4348
- if (filter && !filter(shape)) return false;
4349
- return true;
4350
- });
4346
+ const shapesToCheck = opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted();
4351
4347
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
4352
4348
  const shape = shapesToCheck[i];
4349
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4350
+ if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group")) {
4351
+ continue;
4352
+ }
4353
+ const pageMask = this.getShapeMask(shape);
4354
+ if (pageMask && !pointInPolygon(point, pageMask)) continue;
4355
+ if (filter && !filter(shape)) continue;
4353
4356
  const geometry = this.getShapeGeometry(shape);
4354
4357
  const isGroup = geometry instanceof Group2d;
4355
4358
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
@@ -4454,11 +4457,15 @@ class Editor extends EventEmitter {
4454
4457
  getShapesAtPoint(point, opts = {}) {
4455
4458
  const margin = opts.margin ?? 0;
4456
4459
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin);
4457
- return this.getCurrentPageShapesSorted().filter((shape) => {
4458
- if (this.isShapeHidden(shape)) return false;
4459
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4460
- return this.isPointInShape(shape, point, opts);
4461
- }).reverse();
4460
+ const sorted = this.getCurrentPageShapesSorted();
4461
+ const result = [];
4462
+ for (let i = sorted.length - 1; i >= 0; i--) {
4463
+ const shape = sorted[i];
4464
+ if (this.isShapeHidden(shape)) continue;
4465
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4466
+ if (this.isPointInShape(shape, point, opts)) result.push(shape);
4467
+ }
4468
+ return result;
4462
4469
  }
4463
4470
  /**
4464
4471
  * Get shape IDs within the given bounds.
@@ -5754,6 +5761,9 @@ class Editor extends EventEmitter {
5754
5761
  */
5755
5762
  alignShapes(shapes, operation) {
5756
5763
  if (this.getIsReadonly()) return this;
5764
+ if (operation === "center") {
5765
+ return this.alignShapes(shapes, "center-horizontal").alignShapes(shapes, "center-vertical");
5766
+ }
5757
5767
  const { clusters: shapeClustersToAlign, allBounds } = this.getShapeClusters(shapes, "align");
5758
5768
  if (shapeClustersToAlign.length < 2) return this;
5759
5769
  const commonBounds = Box.Common(allBounds);
@@ -7899,7 +7909,8 @@ class Editor extends EventEmitter {
7899
7909
  _prevCursor = "default";
7900
7910
  /** @internal */
7901
7911
  _shiftKeyTimeout = -1;
7902
- _setShiftKeyTimeout() {
7912
+ _releaseShiftKey() {
7913
+ this._shiftKeyTimeout = -1;
7903
7914
  this.inputs.setShiftKey(false);
7904
7915
  this.dispatch({
7905
7916
  type: "keyboard",
@@ -7915,7 +7926,8 @@ class Editor extends EventEmitter {
7915
7926
  }
7916
7927
  /** @internal */
7917
7928
  _altKeyTimeout = -1;
7918
- _setAltKeyTimeout() {
7929
+ _releaseAltKey() {
7930
+ this._altKeyTimeout = -1;
7919
7931
  this.inputs.setAltKey(false);
7920
7932
  this.dispatch({
7921
7933
  type: "keyboard",
@@ -7931,7 +7943,8 @@ class Editor extends EventEmitter {
7931
7943
  }
7932
7944
  /** @internal */
7933
7945
  _ctrlKeyTimeout = -1;
7934
- _setCtrlKeyTimeout() {
7946
+ _releaseCtrlKey() {
7947
+ this._ctrlKeyTimeout = -1;
7935
7948
  this.inputs.setCtrlKey(false);
7936
7949
  this.dispatch({
7937
7950
  type: "keyboard",
@@ -7947,7 +7960,8 @@ class Editor extends EventEmitter {
7947
7960
  }
7948
7961
  /** @internal */
7949
7962
  _metaKeyTimeout = -1;
7950
- _setMetaKeyTimeout() {
7963
+ _releaseMetaKey() {
7964
+ this._metaKeyTimeout = -1;
7951
7965
  this.inputs.setMetaKey(false);
7952
7966
  this.dispatch({
7953
7967
  type: "keyboard",
@@ -7961,6 +7975,51 @@ class Editor extends EventEmitter {
7961
7975
  code: "MetaLeft"
7962
7976
  });
7963
7977
  }
7978
+ /**
7979
+ * Flush any modifier still sitting in its 150ms release-debounce window, so a new
7980
+ * interaction (a pointer down) starts with correct modifier state instead of a
7981
+ * just-released key still being counted as held. A pending timer is exactly
7982
+ * "released but still counted as held"; a genuinely-held modifier has no timer
7983
+ * (-1) and is left alone.
7984
+ *
7985
+ * Each modifier is released through the same path its timer would have taken
7986
+ * (`_releaseShiftKey` and friends), which dispatches the synthetic `key_up` so
7987
+ * stale codes (e.g. `ShiftLeft`) leave `inputs.keys` and tool `onKeyUp` handlers run.
7988
+ * We clear every pending modifier atom and timer *first*, then dispatch: a synthetic
7989
+ * `key_up` reports all currently-held modifiers, so releasing them one at a time
7990
+ * would make each event re-confirm the not-yet-released ones and cancel their flush.
7991
+ * @internal
7992
+ */
7993
+ _releaseDebouncedModifiers() {
7994
+ const releaseShift = this._shiftKeyTimeout !== -1;
7995
+ const releaseAlt = this._altKeyTimeout !== -1;
7996
+ const releaseCtrl = this._ctrlKeyTimeout !== -1;
7997
+ const releaseMeta = this._metaKeyTimeout !== -1;
7998
+ if (releaseShift) {
7999
+ clearTimeout(this._shiftKeyTimeout);
8000
+ this._shiftKeyTimeout = -1;
8001
+ this.inputs.setShiftKey(false);
8002
+ }
8003
+ if (releaseAlt) {
8004
+ clearTimeout(this._altKeyTimeout);
8005
+ this._altKeyTimeout = -1;
8006
+ this.inputs.setAltKey(false);
8007
+ }
8008
+ if (releaseCtrl) {
8009
+ clearTimeout(this._ctrlKeyTimeout);
8010
+ this._ctrlKeyTimeout = -1;
8011
+ this.inputs.setCtrlKey(false);
8012
+ }
8013
+ if (releaseMeta) {
8014
+ clearTimeout(this._metaKeyTimeout);
8015
+ this._metaKeyTimeout = -1;
8016
+ this.inputs.setMetaKey(false);
8017
+ }
8018
+ if (releaseShift) this._releaseShiftKey();
8019
+ if (releaseAlt) this._releaseAltKey();
8020
+ if (releaseCtrl) this._releaseCtrlKey();
8021
+ if (releaseMeta) this._releaseMetaKey();
8022
+ }
7964
8023
  /** @internal */
7965
8024
  _restoreToolId = "select";
7966
8025
  /** @internal */
@@ -8070,28 +8129,28 @@ class Editor extends EventEmitter {
8070
8129
  this._shiftKeyTimeout = -1;
8071
8130
  inputs.setShiftKey(true);
8072
8131
  } else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
8073
- this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150);
8132
+ this._shiftKeyTimeout = this.timers.setTimeout(this._releaseShiftKey, 150);
8074
8133
  }
8075
8134
  if (info.altKey) {
8076
8135
  clearTimeout(this._altKeyTimeout);
8077
8136
  this._altKeyTimeout = -1;
8078
8137
  inputs.setAltKey(true);
8079
8138
  } else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
8080
- this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150);
8139
+ this._altKeyTimeout = this.timers.setTimeout(this._releaseAltKey, 150);
8081
8140
  }
8082
8141
  if (info.ctrlKey) {
8083
8142
  clearTimeout(this._ctrlKeyTimeout);
8084
8143
  this._ctrlKeyTimeout = -1;
8085
8144
  inputs.setCtrlKey(true);
8086
8145
  } else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
8087
- this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150);
8146
+ this._ctrlKeyTimeout = this.timers.setTimeout(this._releaseCtrlKey, 150);
8088
8147
  }
8089
8148
  if (info.metaKey && info.name !== "key_up") {
8090
8149
  clearTimeout(this._metaKeyTimeout);
8091
8150
  this._metaKeyTimeout = -1;
8092
8151
  inputs.setMetaKey(true);
8093
8152
  } else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
8094
- this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150);
8153
+ this._metaKeyTimeout = this.timers.setTimeout(this._releaseMetaKey, 150);
8095
8154
  }
8096
8155
  if (!inputs.getIsPointing()) {
8097
8156
  inputs.setIsDragging(false);
@@ -8236,6 +8295,7 @@ class Editor extends EventEmitter {
8236
8295
  switch (info.name) {
8237
8296
  case "pointer_down": {
8238
8297
  if (isPenMode && !isPen) return;
8298
+ this._releaseDebouncedModifiers();
8239
8299
  if (!this.inputs.getIsPanning()) {
8240
8300
  this._longPressTimeout = this.timers.setTimeout(() => {
8241
8301
  const vsb = this.getViewportScreenBounds();
@@ -8718,16 +8778,16 @@ __decorateClass([
8718
8778
  ], Editor.prototype, "getIsReadonly", 1);
8719
8779
  __decorateClass([
8720
8780
  bind
8721
- ], Editor.prototype, "_setShiftKeyTimeout", 1);
8781
+ ], Editor.prototype, "_releaseShiftKey", 1);
8722
8782
  __decorateClass([
8723
8783
  bind
8724
- ], Editor.prototype, "_setAltKeyTimeout", 1);
8784
+ ], Editor.prototype, "_releaseAltKey", 1);
8725
8785
  __decorateClass([
8726
8786
  bind
8727
- ], Editor.prototype, "_setCtrlKeyTimeout", 1);
8787
+ ], Editor.prototype, "_releaseCtrlKey", 1);
8728
8788
  __decorateClass([
8729
8789
  bind
8730
- ], Editor.prototype, "_setMetaKeyTimeout", 1);
8790
+ ], Editor.prototype, "_releaseMetaKey", 1);
8731
8791
  function alertMaxShapes(editor, pageId = editor.getCurrentPageId()) {
8732
8792
  const name = editor.getPage(pageId).name;
8733
8793
  editor.emit("max-shapes", { name, pageId, count: editor.options.maxShapesPerPage });