@tldraw/editor 5.3.0 → 5.4.0-canary.0714aa9af9a0

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 (54) hide show
  1. package/dist-cjs/index.d.ts +62 -52
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +82 -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/hooks/useDocumentEvents.js +3 -2
  13. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  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 +62 -52
  22. package/dist-esm/index.mjs +1 -1
  23. package/dist-esm/index.mjs.map +2 -2
  24. package/dist-esm/lib/editor/Editor.mjs +82 -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/hooks/useDocumentEvents.mjs +3 -2
  33. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  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 +1 -0
  43. package/src/lib/editor/Editor.ts +120 -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/hooks/useDocumentEvents.ts +9 -8
  50. package/src/lib/hooks/useViewportHeight.ts +9 -1
  51. package/src/lib/primitives/geometry/Group2d.ts +63 -27
  52. package/src/lib/utils/normalizeWheel.test.ts +39 -0
  53. package/src/lib/utils/normalizeWheel.ts +6 -0
  54. package/src/version.ts +3 -3
@@ -4331,17 +4331,16 @@ class Editor extends EventEmitter {
4331
4331
  let inMarginClosestToEdgeHit = null;
4332
4332
  const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin());
4333
4333
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin);
4334
- const shapesToCheck = (opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted()).filter((shape) => {
4335
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4336
- if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group"))
4337
- return false;
4338
- const pageMask = this.getShapeMask(shape);
4339
- if (pageMask && !pointInPolygon(point, pageMask)) return false;
4340
- if (filter && !filter(shape)) return false;
4341
- return true;
4342
- });
4334
+ const shapesToCheck = opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted();
4343
4335
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
4344
4336
  const shape = shapesToCheck[i];
4337
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4338
+ if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group")) {
4339
+ continue;
4340
+ }
4341
+ const pageMask = this.getShapeMask(shape);
4342
+ if (pageMask && !pointInPolygon(point, pageMask)) continue;
4343
+ if (filter && !filter(shape)) continue;
4345
4344
  const geometry = this.getShapeGeometry(shape);
4346
4345
  const isGroup = geometry instanceof Group2d;
4347
4346
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
@@ -4446,11 +4445,15 @@ class Editor extends EventEmitter {
4446
4445
  getShapesAtPoint(point, opts = {}) {
4447
4446
  const margin = opts.margin ?? 0;
4448
4447
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin);
4449
- return this.getCurrentPageShapesSorted().filter((shape) => {
4450
- if (this.isShapeHidden(shape)) return false;
4451
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4452
- return this.isPointInShape(shape, point, opts);
4453
- }).reverse();
4448
+ const sorted = this.getCurrentPageShapesSorted();
4449
+ const result = [];
4450
+ for (let i = sorted.length - 1; i >= 0; i--) {
4451
+ const shape = sorted[i];
4452
+ if (this.isShapeHidden(shape)) continue;
4453
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4454
+ if (this.isPointInShape(shape, point, opts)) result.push(shape);
4455
+ }
4456
+ return result;
4454
4457
  }
4455
4458
  /**
4456
4459
  * Get shape IDs within the given bounds.
@@ -5746,6 +5749,9 @@ class Editor extends EventEmitter {
5746
5749
  */
5747
5750
  alignShapes(shapes, operation) {
5748
5751
  if (this.getIsReadonly()) return this;
5752
+ if (operation === "center") {
5753
+ return this.alignShapes(shapes, "center-horizontal").alignShapes(shapes, "center-vertical");
5754
+ }
5749
5755
  const { clusters: shapeClustersToAlign, allBounds } = this.getShapeClusters(shapes, "align");
5750
5756
  if (shapeClustersToAlign.length < 2) return this;
5751
5757
  const commonBounds = Box.Common(allBounds);
@@ -7891,7 +7897,8 @@ class Editor extends EventEmitter {
7891
7897
  _prevCursor = "default";
7892
7898
  /** @internal */
7893
7899
  _shiftKeyTimeout = -1;
7894
- _setShiftKeyTimeout() {
7900
+ _releaseShiftKey() {
7901
+ this._shiftKeyTimeout = -1;
7895
7902
  this.inputs.setShiftKey(false);
7896
7903
  this.dispatch({
7897
7904
  type: "keyboard",
@@ -7907,7 +7914,8 @@ class Editor extends EventEmitter {
7907
7914
  }
7908
7915
  /** @internal */
7909
7916
  _altKeyTimeout = -1;
7910
- _setAltKeyTimeout() {
7917
+ _releaseAltKey() {
7918
+ this._altKeyTimeout = -1;
7911
7919
  this.inputs.setAltKey(false);
7912
7920
  this.dispatch({
7913
7921
  type: "keyboard",
@@ -7923,7 +7931,8 @@ class Editor extends EventEmitter {
7923
7931
  }
7924
7932
  /** @internal */
7925
7933
  _ctrlKeyTimeout = -1;
7926
- _setCtrlKeyTimeout() {
7934
+ _releaseCtrlKey() {
7935
+ this._ctrlKeyTimeout = -1;
7927
7936
  this.inputs.setCtrlKey(false);
7928
7937
  this.dispatch({
7929
7938
  type: "keyboard",
@@ -7939,7 +7948,8 @@ class Editor extends EventEmitter {
7939
7948
  }
7940
7949
  /** @internal */
7941
7950
  _metaKeyTimeout = -1;
7942
- _setMetaKeyTimeout() {
7951
+ _releaseMetaKey() {
7952
+ this._metaKeyTimeout = -1;
7943
7953
  this.inputs.setMetaKey(false);
7944
7954
  this.dispatch({
7945
7955
  type: "keyboard",
@@ -7953,6 +7963,51 @@ class Editor extends EventEmitter {
7953
7963
  code: "MetaLeft"
7954
7964
  });
7955
7965
  }
7966
+ /**
7967
+ * Flush any modifier still sitting in its 150ms release-debounce window, so a new
7968
+ * interaction (a pointer down) starts with correct modifier state instead of a
7969
+ * just-released key still being counted as held. A pending timer is exactly
7970
+ * "released but still counted as held"; a genuinely-held modifier has no timer
7971
+ * (-1) and is left alone.
7972
+ *
7973
+ * Each modifier is released through the same path its timer would have taken
7974
+ * (`_releaseShiftKey` and friends), which dispatches the synthetic `key_up` so
7975
+ * stale codes (e.g. `ShiftLeft`) leave `inputs.keys` and tool `onKeyUp` handlers run.
7976
+ * We clear every pending modifier atom and timer *first*, then dispatch: a synthetic
7977
+ * `key_up` reports all currently-held modifiers, so releasing them one at a time
7978
+ * would make each event re-confirm the not-yet-released ones and cancel their flush.
7979
+ * @internal
7980
+ */
7981
+ _releaseDebouncedModifiers() {
7982
+ const releaseShift = this._shiftKeyTimeout !== -1;
7983
+ const releaseAlt = this._altKeyTimeout !== -1;
7984
+ const releaseCtrl = this._ctrlKeyTimeout !== -1;
7985
+ const releaseMeta = this._metaKeyTimeout !== -1;
7986
+ if (releaseShift) {
7987
+ clearTimeout(this._shiftKeyTimeout);
7988
+ this._shiftKeyTimeout = -1;
7989
+ this.inputs.setShiftKey(false);
7990
+ }
7991
+ if (releaseAlt) {
7992
+ clearTimeout(this._altKeyTimeout);
7993
+ this._altKeyTimeout = -1;
7994
+ this.inputs.setAltKey(false);
7995
+ }
7996
+ if (releaseCtrl) {
7997
+ clearTimeout(this._ctrlKeyTimeout);
7998
+ this._ctrlKeyTimeout = -1;
7999
+ this.inputs.setCtrlKey(false);
8000
+ }
8001
+ if (releaseMeta) {
8002
+ clearTimeout(this._metaKeyTimeout);
8003
+ this._metaKeyTimeout = -1;
8004
+ this.inputs.setMetaKey(false);
8005
+ }
8006
+ if (releaseShift) this._releaseShiftKey();
8007
+ if (releaseAlt) this._releaseAltKey();
8008
+ if (releaseCtrl) this._releaseCtrlKey();
8009
+ if (releaseMeta) this._releaseMetaKey();
8010
+ }
7956
8011
  /** @internal */
7957
8012
  _restoreToolId = "select";
7958
8013
  /** @internal */
@@ -8062,28 +8117,28 @@ class Editor extends EventEmitter {
8062
8117
  this._shiftKeyTimeout = -1;
8063
8118
  inputs.setShiftKey(true);
8064
8119
  } else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
8065
- this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150);
8120
+ this._shiftKeyTimeout = this.timers.setTimeout(this._releaseShiftKey, 150);
8066
8121
  }
8067
8122
  if (info.altKey) {
8068
8123
  clearTimeout(this._altKeyTimeout);
8069
8124
  this._altKeyTimeout = -1;
8070
8125
  inputs.setAltKey(true);
8071
8126
  } else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
8072
- this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150);
8127
+ this._altKeyTimeout = this.timers.setTimeout(this._releaseAltKey, 150);
8073
8128
  }
8074
8129
  if (info.ctrlKey) {
8075
8130
  clearTimeout(this._ctrlKeyTimeout);
8076
8131
  this._ctrlKeyTimeout = -1;
8077
8132
  inputs.setCtrlKey(true);
8078
8133
  } else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
8079
- this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150);
8134
+ this._ctrlKeyTimeout = this.timers.setTimeout(this._releaseCtrlKey, 150);
8080
8135
  }
8081
8136
  if (info.metaKey && info.name !== "key_up") {
8082
8137
  clearTimeout(this._metaKeyTimeout);
8083
8138
  this._metaKeyTimeout = -1;
8084
8139
  inputs.setMetaKey(true);
8085
8140
  } else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
8086
- this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150);
8141
+ this._metaKeyTimeout = this.timers.setTimeout(this._releaseMetaKey, 150);
8087
8142
  }
8088
8143
  if (!inputs.getIsPointing()) {
8089
8144
  inputs.setIsDragging(false);
@@ -8228,6 +8283,7 @@ class Editor extends EventEmitter {
8228
8283
  switch (info.name) {
8229
8284
  case "pointer_down": {
8230
8285
  if (isPenMode && !isPen) return;
8286
+ this._releaseDebouncedModifiers();
8231
8287
  if (!this.inputs.getIsPanning()) {
8232
8288
  this._longPressTimeout = this.timers.setTimeout(() => {
8233
8289
  const vsb = this.getViewportScreenBounds();
@@ -8710,16 +8766,16 @@ __decorateClass([
8710
8766
  ], Editor.prototype, "getIsReadonly", 1);
8711
8767
  __decorateClass([
8712
8768
  bind
8713
- ], Editor.prototype, "_setShiftKeyTimeout", 1);
8769
+ ], Editor.prototype, "_releaseShiftKey", 1);
8714
8770
  __decorateClass([
8715
8771
  bind
8716
- ], Editor.prototype, "_setAltKeyTimeout", 1);
8772
+ ], Editor.prototype, "_releaseAltKey", 1);
8717
8773
  __decorateClass([
8718
8774
  bind
8719
- ], Editor.prototype, "_setCtrlKeyTimeout", 1);
8775
+ ], Editor.prototype, "_releaseCtrlKey", 1);
8720
8776
  __decorateClass([
8721
8777
  bind
8722
- ], Editor.prototype, "_setMetaKeyTimeout", 1);
8778
+ ], Editor.prototype, "_releaseMetaKey", 1);
8723
8779
  function alertMaxShapes(editor, pageId = editor.getCurrentPageId()) {
8724
8780
  const name = editor.getPage(pageId).name;
8725
8781
  editor.emit("max-shapes", { name, pageId, count: editor.options.maxShapesPerPage });