@tldraw/editor 5.3.2 → 5.4.0-canary.02cd0bd3b597

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
@@ -53,6 +53,7 @@ var import_constants = require("../constants");
53
53
  var import_domUtils = require("../exports/domUtils");
54
54
  var import_exportToSvg = require("../exports/exportToSvg");
55
55
  var import_getSvgAsImage = require("../exports/getSvgAsImage");
56
+ var import_editors = require("../globals/editors");
56
57
  var import_menus = require("../globals/menus");
57
58
  var import_time = require("../globals/time");
58
59
  var import_options = require("../options");
@@ -571,9 +572,11 @@ class Editor extends import_eventemitter3.default {
571
572
  this.on("tick", this._flushEventsForTick);
572
573
  this.on("mount", () => {
573
574
  this._isMounted.set(true);
575
+ (0, import_editors.registerMountedEditor)(this);
574
576
  });
575
577
  this.on("unmount", () => {
576
578
  this._isMounted.set(false);
579
+ (0, import_editors.unregisterMountedEditor)(this);
577
580
  });
578
581
  this.timers.requestAnimationFrame(() => {
579
582
  this._tickManager.start();
@@ -815,6 +818,7 @@ class Editor extends import_eventemitter3.default {
815
818
  if (this._isMounted.get()) {
816
819
  this.emit("unmount");
817
820
  }
821
+ (0, import_editors.unregisterMountedEditor)(this);
818
822
  this.stopCameraAnimation();
819
823
  if (this.getInstanceState().followingUserId) {
820
824
  this.stopFollowingUser();
@@ -4306,17 +4310,16 @@ class Editor extends import_eventemitter3.default {
4306
4310
  let inMarginClosestToEdgeHit = null;
4307
4311
  const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin());
4308
4312
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin);
4309
- const shapesToCheck = (opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted()).filter((shape) => {
4310
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4311
- if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group"))
4312
- return false;
4313
- const pageMask = this.getShapeMask(shape);
4314
- if (pageMask && !(0, import_utils2.pointInPolygon)(point, pageMask)) return false;
4315
- if (filter && !filter(shape)) return false;
4316
- return true;
4317
- });
4313
+ const shapesToCheck = opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted();
4318
4314
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
4319
4315
  const shape = shapesToCheck[i];
4316
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4317
+ if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group")) {
4318
+ continue;
4319
+ }
4320
+ const pageMask = this.getShapeMask(shape);
4321
+ if (pageMask && !(0, import_utils2.pointInPolygon)(point, pageMask)) continue;
4322
+ if (filter && !filter(shape)) continue;
4320
4323
  const geometry = this.getShapeGeometry(shape);
4321
4324
  const isGroup = geometry instanceof import_Group2d.Group2d;
4322
4325
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
@@ -4421,11 +4424,15 @@ class Editor extends import_eventemitter3.default {
4421
4424
  getShapesAtPoint(point, opts = {}) {
4422
4425
  const margin = opts.margin ?? 0;
4423
4426
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin);
4424
- return this.getCurrentPageShapesSorted().filter((shape) => {
4425
- if (this.isShapeHidden(shape)) return false;
4426
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4427
- return this.isPointInShape(shape, point, opts);
4428
- }).reverse();
4427
+ const sorted = this.getCurrentPageShapesSorted();
4428
+ const result = [];
4429
+ for (let i = sorted.length - 1; i >= 0; i--) {
4430
+ const shape = sorted[i];
4431
+ if (this.isShapeHidden(shape)) continue;
4432
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4433
+ if (this.isPointInShape(shape, point, opts)) result.push(shape);
4434
+ }
4435
+ return result;
4429
4436
  }
4430
4437
  /**
4431
4438
  * Get shape IDs within the given bounds.
@@ -5721,6 +5728,9 @@ class Editor extends import_eventemitter3.default {
5721
5728
  */
5722
5729
  alignShapes(shapes, operation) {
5723
5730
  if (this.getIsReadonly()) return this;
5731
+ if (operation === "center") {
5732
+ return this.alignShapes(shapes, "center-horizontal").alignShapes(shapes, "center-vertical");
5733
+ }
5724
5734
  const { clusters: shapeClustersToAlign, allBounds } = this.getShapeClusters(shapes, "align");
5725
5735
  if (shapeClustersToAlign.length < 2) return this;
5726
5736
  const commonBounds = import_Box.Box.Common(allBounds);
@@ -7866,7 +7876,8 @@ class Editor extends import_eventemitter3.default {
7866
7876
  _prevCursor = "default";
7867
7877
  /** @internal */
7868
7878
  _shiftKeyTimeout = -1;
7869
- _setShiftKeyTimeout() {
7879
+ _releaseShiftKey() {
7880
+ this._shiftKeyTimeout = -1;
7870
7881
  this.inputs.setShiftKey(false);
7871
7882
  this.dispatch({
7872
7883
  type: "keyboard",
@@ -7882,7 +7893,8 @@ class Editor extends import_eventemitter3.default {
7882
7893
  }
7883
7894
  /** @internal */
7884
7895
  _altKeyTimeout = -1;
7885
- _setAltKeyTimeout() {
7896
+ _releaseAltKey() {
7897
+ this._altKeyTimeout = -1;
7886
7898
  this.inputs.setAltKey(false);
7887
7899
  this.dispatch({
7888
7900
  type: "keyboard",
@@ -7898,7 +7910,8 @@ class Editor extends import_eventemitter3.default {
7898
7910
  }
7899
7911
  /** @internal */
7900
7912
  _ctrlKeyTimeout = -1;
7901
- _setCtrlKeyTimeout() {
7913
+ _releaseCtrlKey() {
7914
+ this._ctrlKeyTimeout = -1;
7902
7915
  this.inputs.setCtrlKey(false);
7903
7916
  this.dispatch({
7904
7917
  type: "keyboard",
@@ -7914,7 +7927,8 @@ class Editor extends import_eventemitter3.default {
7914
7927
  }
7915
7928
  /** @internal */
7916
7929
  _metaKeyTimeout = -1;
7917
- _setMetaKeyTimeout() {
7930
+ _releaseMetaKey() {
7931
+ this._metaKeyTimeout = -1;
7918
7932
  this.inputs.setMetaKey(false);
7919
7933
  this.dispatch({
7920
7934
  type: "keyboard",
@@ -7928,6 +7942,51 @@ class Editor extends import_eventemitter3.default {
7928
7942
  code: "MetaLeft"
7929
7943
  });
7930
7944
  }
7945
+ /**
7946
+ * Flush any modifier still sitting in its 150ms release-debounce window, so a new
7947
+ * interaction (a pointer down) starts with correct modifier state instead of a
7948
+ * just-released key still being counted as held. A pending timer is exactly
7949
+ * "released but still counted as held"; a genuinely-held modifier has no timer
7950
+ * (-1) and is left alone.
7951
+ *
7952
+ * Each modifier is released through the same path its timer would have taken
7953
+ * (`_releaseShiftKey` and friends), which dispatches the synthetic `key_up` so
7954
+ * stale codes (e.g. `ShiftLeft`) leave `inputs.keys` and tool `onKeyUp` handlers run.
7955
+ * We clear every pending modifier atom and timer *first*, then dispatch: a synthetic
7956
+ * `key_up` reports all currently-held modifiers, so releasing them one at a time
7957
+ * would make each event re-confirm the not-yet-released ones and cancel their flush.
7958
+ * @internal
7959
+ */
7960
+ _releaseDebouncedModifiers() {
7961
+ const releaseShift = this._shiftKeyTimeout !== -1;
7962
+ const releaseAlt = this._altKeyTimeout !== -1;
7963
+ const releaseCtrl = this._ctrlKeyTimeout !== -1;
7964
+ const releaseMeta = this._metaKeyTimeout !== -1;
7965
+ if (releaseShift) {
7966
+ clearTimeout(this._shiftKeyTimeout);
7967
+ this._shiftKeyTimeout = -1;
7968
+ this.inputs.setShiftKey(false);
7969
+ }
7970
+ if (releaseAlt) {
7971
+ clearTimeout(this._altKeyTimeout);
7972
+ this._altKeyTimeout = -1;
7973
+ this.inputs.setAltKey(false);
7974
+ }
7975
+ if (releaseCtrl) {
7976
+ clearTimeout(this._ctrlKeyTimeout);
7977
+ this._ctrlKeyTimeout = -1;
7978
+ this.inputs.setCtrlKey(false);
7979
+ }
7980
+ if (releaseMeta) {
7981
+ clearTimeout(this._metaKeyTimeout);
7982
+ this._metaKeyTimeout = -1;
7983
+ this.inputs.setMetaKey(false);
7984
+ }
7985
+ if (releaseShift) this._releaseShiftKey();
7986
+ if (releaseAlt) this._releaseAltKey();
7987
+ if (releaseCtrl) this._releaseCtrlKey();
7988
+ if (releaseMeta) this._releaseMetaKey();
7989
+ }
7931
7990
  /** @internal */
7932
7991
  _restoreToolId = "select";
7933
7992
  /** @internal */
@@ -8037,28 +8096,28 @@ class Editor extends import_eventemitter3.default {
8037
8096
  this._shiftKeyTimeout = -1;
8038
8097
  inputs.setShiftKey(true);
8039
8098
  } else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
8040
- this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150);
8099
+ this._shiftKeyTimeout = this.timers.setTimeout(this._releaseShiftKey, 150);
8041
8100
  }
8042
8101
  if (info.altKey) {
8043
8102
  clearTimeout(this._altKeyTimeout);
8044
8103
  this._altKeyTimeout = -1;
8045
8104
  inputs.setAltKey(true);
8046
8105
  } else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
8047
- this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150);
8106
+ this._altKeyTimeout = this.timers.setTimeout(this._releaseAltKey, 150);
8048
8107
  }
8049
8108
  if (info.ctrlKey) {
8050
8109
  clearTimeout(this._ctrlKeyTimeout);
8051
8110
  this._ctrlKeyTimeout = -1;
8052
8111
  inputs.setCtrlKey(true);
8053
8112
  } else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
8054
- this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150);
8113
+ this._ctrlKeyTimeout = this.timers.setTimeout(this._releaseCtrlKey, 150);
8055
8114
  }
8056
8115
  if (info.metaKey && info.name !== "key_up") {
8057
8116
  clearTimeout(this._metaKeyTimeout);
8058
8117
  this._metaKeyTimeout = -1;
8059
8118
  inputs.setMetaKey(true);
8060
8119
  } else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
8061
- this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150);
8120
+ this._metaKeyTimeout = this.timers.setTimeout(this._releaseMetaKey, 150);
8062
8121
  }
8063
8122
  if (!inputs.getIsPointing()) {
8064
8123
  inputs.setIsDragging(false);
@@ -8203,6 +8262,7 @@ class Editor extends import_eventemitter3.default {
8203
8262
  switch (info.name) {
8204
8263
  case "pointer_down": {
8205
8264
  if (isPenMode && !isPen) return;
8265
+ this._releaseDebouncedModifiers();
8206
8266
  if (!this.inputs.getIsPanning()) {
8207
8267
  this._longPressTimeout = this.timers.setTimeout(() => {
8208
8268
  const vsb = this.getViewportScreenBounds();
@@ -8685,16 +8745,16 @@ __decorateClass([
8685
8745
  ], Editor.prototype, "getIsReadonly", 1);
8686
8746
  __decorateClass([
8687
8747
  import_utils.bind
8688
- ], Editor.prototype, "_setShiftKeyTimeout", 1);
8748
+ ], Editor.prototype, "_releaseShiftKey", 1);
8689
8749
  __decorateClass([
8690
8750
  import_utils.bind
8691
- ], Editor.prototype, "_setAltKeyTimeout", 1);
8751
+ ], Editor.prototype, "_releaseAltKey", 1);
8692
8752
  __decorateClass([
8693
8753
  import_utils.bind
8694
- ], Editor.prototype, "_setCtrlKeyTimeout", 1);
8754
+ ], Editor.prototype, "_releaseCtrlKey", 1);
8695
8755
  __decorateClass([
8696
8756
  import_utils.bind
8697
- ], Editor.prototype, "_setMetaKeyTimeout", 1);
8757
+ ], Editor.prototype, "_releaseMetaKey", 1);
8698
8758
  function alertMaxShapes(editor, pageId = editor.getCurrentPageId()) {
8699
8759
  const name = editor.getPage(pageId).name;
8700
8760
  editor.emit("max-shapes", { name, pageId, count: editor.options.maxShapesPerPage });