@tldraw/editor 5.3.1 → 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 (49) 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/useViewportHeight.js.map +2 -2
  13. package/dist-cjs/lib/primitives/geometry/Group2d.js +63 -21
  14. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  15. package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
  16. package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
  17. package/dist-cjs/version.js +3 -3
  18. package/dist-cjs/version.js.map +1 -1
  19. package/dist-esm/index.d.mts +62 -52
  20. package/dist-esm/index.mjs +1 -1
  21. package/dist-esm/index.mjs.map +2 -2
  22. package/dist-esm/lib/editor/Editor.mjs +82 -26
  23. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  24. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
  25. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  26. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -4
  27. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  28. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  29. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  30. package/dist-esm/lib/hooks/useViewportHeight.mjs.map +2 -2
  31. package/dist-esm/lib/primitives/geometry/Group2d.mjs +63 -21
  32. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  33. package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
  34. package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
  35. package/dist-esm/version.mjs +3 -3
  36. package/dist-esm/version.mjs.map +1 -1
  37. package/package.json +7 -7
  38. package/src/index.ts +1 -0
  39. package/src/lib/editor/Editor.ts +120 -37
  40. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
  41. package/src/lib/editor/managers/TextManager/TextManager.test.ts +42 -0
  42. package/src/lib/editor/managers/TextManager/TextManager.ts +10 -8
  43. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  44. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  45. package/src/lib/hooks/useViewportHeight.ts +9 -1
  46. package/src/lib/primitives/geometry/Group2d.ts +63 -27
  47. package/src/lib/utils/normalizeWheel.test.ts +39 -0
  48. package/src/lib/utils/normalizeWheel.ts +6 -0
  49. package/src/version.ts +3 -3
@@ -4298,17 +4298,16 @@ class Editor extends import_eventemitter3.default {
4298
4298
  let inMarginClosestToEdgeHit = null;
4299
4299
  const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin());
4300
4300
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin);
4301
- const shapesToCheck = (opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted()).filter((shape) => {
4302
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4303
- if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group"))
4304
- return false;
4305
- const pageMask = this.getShapeMask(shape);
4306
- if (pageMask && !(0, import_utils2.pointInPolygon)(point, pageMask)) return false;
4307
- if (filter && !filter(shape)) return false;
4308
- return true;
4309
- });
4301
+ const shapesToCheck = opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted();
4310
4302
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
4311
4303
  const shape = shapesToCheck[i];
4304
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4305
+ if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group")) {
4306
+ continue;
4307
+ }
4308
+ const pageMask = this.getShapeMask(shape);
4309
+ if (pageMask && !(0, import_utils2.pointInPolygon)(point, pageMask)) continue;
4310
+ if (filter && !filter(shape)) continue;
4312
4311
  const geometry = this.getShapeGeometry(shape);
4313
4312
  const isGroup = geometry instanceof import_Group2d.Group2d;
4314
4313
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
@@ -4413,11 +4412,15 @@ class Editor extends import_eventemitter3.default {
4413
4412
  getShapesAtPoint(point, opts = {}) {
4414
4413
  const margin = opts.margin ?? 0;
4415
4414
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin);
4416
- return this.getCurrentPageShapesSorted().filter((shape) => {
4417
- if (this.isShapeHidden(shape)) return false;
4418
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4419
- return this.isPointInShape(shape, point, opts);
4420
- }).reverse();
4415
+ const sorted = this.getCurrentPageShapesSorted();
4416
+ const result = [];
4417
+ for (let i = sorted.length - 1; i >= 0; i--) {
4418
+ const shape = sorted[i];
4419
+ if (this.isShapeHidden(shape)) continue;
4420
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4421
+ if (this.isPointInShape(shape, point, opts)) result.push(shape);
4422
+ }
4423
+ return result;
4421
4424
  }
4422
4425
  /**
4423
4426
  * Get shape IDs within the given bounds.
@@ -5713,6 +5716,9 @@ class Editor extends import_eventemitter3.default {
5713
5716
  */
5714
5717
  alignShapes(shapes, operation) {
5715
5718
  if (this.getIsReadonly()) return this;
5719
+ if (operation === "center") {
5720
+ return this.alignShapes(shapes, "center-horizontal").alignShapes(shapes, "center-vertical");
5721
+ }
5716
5722
  const { clusters: shapeClustersToAlign, allBounds } = this.getShapeClusters(shapes, "align");
5717
5723
  if (shapeClustersToAlign.length < 2) return this;
5718
5724
  const commonBounds = import_Box.Box.Common(allBounds);
@@ -7858,7 +7864,8 @@ class Editor extends import_eventemitter3.default {
7858
7864
  _prevCursor = "default";
7859
7865
  /** @internal */
7860
7866
  _shiftKeyTimeout = -1;
7861
- _setShiftKeyTimeout() {
7867
+ _releaseShiftKey() {
7868
+ this._shiftKeyTimeout = -1;
7862
7869
  this.inputs.setShiftKey(false);
7863
7870
  this.dispatch({
7864
7871
  type: "keyboard",
@@ -7874,7 +7881,8 @@ class Editor extends import_eventemitter3.default {
7874
7881
  }
7875
7882
  /** @internal */
7876
7883
  _altKeyTimeout = -1;
7877
- _setAltKeyTimeout() {
7884
+ _releaseAltKey() {
7885
+ this._altKeyTimeout = -1;
7878
7886
  this.inputs.setAltKey(false);
7879
7887
  this.dispatch({
7880
7888
  type: "keyboard",
@@ -7890,7 +7898,8 @@ class Editor extends import_eventemitter3.default {
7890
7898
  }
7891
7899
  /** @internal */
7892
7900
  _ctrlKeyTimeout = -1;
7893
- _setCtrlKeyTimeout() {
7901
+ _releaseCtrlKey() {
7902
+ this._ctrlKeyTimeout = -1;
7894
7903
  this.inputs.setCtrlKey(false);
7895
7904
  this.dispatch({
7896
7905
  type: "keyboard",
@@ -7906,7 +7915,8 @@ class Editor extends import_eventemitter3.default {
7906
7915
  }
7907
7916
  /** @internal */
7908
7917
  _metaKeyTimeout = -1;
7909
- _setMetaKeyTimeout() {
7918
+ _releaseMetaKey() {
7919
+ this._metaKeyTimeout = -1;
7910
7920
  this.inputs.setMetaKey(false);
7911
7921
  this.dispatch({
7912
7922
  type: "keyboard",
@@ -7920,6 +7930,51 @@ class Editor extends import_eventemitter3.default {
7920
7930
  code: "MetaLeft"
7921
7931
  });
7922
7932
  }
7933
+ /**
7934
+ * Flush any modifier still sitting in its 150ms release-debounce window, so a new
7935
+ * interaction (a pointer down) starts with correct modifier state instead of a
7936
+ * just-released key still being counted as held. A pending timer is exactly
7937
+ * "released but still counted as held"; a genuinely-held modifier has no timer
7938
+ * (-1) and is left alone.
7939
+ *
7940
+ * Each modifier is released through the same path its timer would have taken
7941
+ * (`_releaseShiftKey` and friends), which dispatches the synthetic `key_up` so
7942
+ * stale codes (e.g. `ShiftLeft`) leave `inputs.keys` and tool `onKeyUp` handlers run.
7943
+ * We clear every pending modifier atom and timer *first*, then dispatch: a synthetic
7944
+ * `key_up` reports all currently-held modifiers, so releasing them one at a time
7945
+ * would make each event re-confirm the not-yet-released ones and cancel their flush.
7946
+ * @internal
7947
+ */
7948
+ _releaseDebouncedModifiers() {
7949
+ const releaseShift = this._shiftKeyTimeout !== -1;
7950
+ const releaseAlt = this._altKeyTimeout !== -1;
7951
+ const releaseCtrl = this._ctrlKeyTimeout !== -1;
7952
+ const releaseMeta = this._metaKeyTimeout !== -1;
7953
+ if (releaseShift) {
7954
+ clearTimeout(this._shiftKeyTimeout);
7955
+ this._shiftKeyTimeout = -1;
7956
+ this.inputs.setShiftKey(false);
7957
+ }
7958
+ if (releaseAlt) {
7959
+ clearTimeout(this._altKeyTimeout);
7960
+ this._altKeyTimeout = -1;
7961
+ this.inputs.setAltKey(false);
7962
+ }
7963
+ if (releaseCtrl) {
7964
+ clearTimeout(this._ctrlKeyTimeout);
7965
+ this._ctrlKeyTimeout = -1;
7966
+ this.inputs.setCtrlKey(false);
7967
+ }
7968
+ if (releaseMeta) {
7969
+ clearTimeout(this._metaKeyTimeout);
7970
+ this._metaKeyTimeout = -1;
7971
+ this.inputs.setMetaKey(false);
7972
+ }
7973
+ if (releaseShift) this._releaseShiftKey();
7974
+ if (releaseAlt) this._releaseAltKey();
7975
+ if (releaseCtrl) this._releaseCtrlKey();
7976
+ if (releaseMeta) this._releaseMetaKey();
7977
+ }
7923
7978
  /** @internal */
7924
7979
  _restoreToolId = "select";
7925
7980
  /** @internal */
@@ -8029,28 +8084,28 @@ class Editor extends import_eventemitter3.default {
8029
8084
  this._shiftKeyTimeout = -1;
8030
8085
  inputs.setShiftKey(true);
8031
8086
  } else if (!info.shiftKey && inputs.getShiftKey() && this._shiftKeyTimeout === -1) {
8032
- this._shiftKeyTimeout = this.timers.setTimeout(this._setShiftKeyTimeout, 150);
8087
+ this._shiftKeyTimeout = this.timers.setTimeout(this._releaseShiftKey, 150);
8033
8088
  }
8034
8089
  if (info.altKey) {
8035
8090
  clearTimeout(this._altKeyTimeout);
8036
8091
  this._altKeyTimeout = -1;
8037
8092
  inputs.setAltKey(true);
8038
8093
  } else if (!info.altKey && inputs.getAltKey() && this._altKeyTimeout === -1) {
8039
- this._altKeyTimeout = this.timers.setTimeout(this._setAltKeyTimeout, 150);
8094
+ this._altKeyTimeout = this.timers.setTimeout(this._releaseAltKey, 150);
8040
8095
  }
8041
8096
  if (info.ctrlKey) {
8042
8097
  clearTimeout(this._ctrlKeyTimeout);
8043
8098
  this._ctrlKeyTimeout = -1;
8044
8099
  inputs.setCtrlKey(true);
8045
8100
  } else if (!info.ctrlKey && inputs.getCtrlKey() && this._ctrlKeyTimeout === -1) {
8046
- this._ctrlKeyTimeout = this.timers.setTimeout(this._setCtrlKeyTimeout, 150);
8101
+ this._ctrlKeyTimeout = this.timers.setTimeout(this._releaseCtrlKey, 150);
8047
8102
  }
8048
8103
  if (info.metaKey && info.name !== "key_up") {
8049
8104
  clearTimeout(this._metaKeyTimeout);
8050
8105
  this._metaKeyTimeout = -1;
8051
8106
  inputs.setMetaKey(true);
8052
8107
  } else if (!info.metaKey && inputs.getMetaKey() && this._metaKeyTimeout === -1) {
8053
- this._metaKeyTimeout = this.timers.setTimeout(this._setMetaKeyTimeout, 150);
8108
+ this._metaKeyTimeout = this.timers.setTimeout(this._releaseMetaKey, 150);
8054
8109
  }
8055
8110
  if (!inputs.getIsPointing()) {
8056
8111
  inputs.setIsDragging(false);
@@ -8195,6 +8250,7 @@ class Editor extends import_eventemitter3.default {
8195
8250
  switch (info.name) {
8196
8251
  case "pointer_down": {
8197
8252
  if (isPenMode && !isPen) return;
8253
+ this._releaseDebouncedModifiers();
8198
8254
  if (!this.inputs.getIsPanning()) {
8199
8255
  this._longPressTimeout = this.timers.setTimeout(() => {
8200
8256
  const vsb = this.getViewportScreenBounds();
@@ -8677,16 +8733,16 @@ __decorateClass([
8677
8733
  ], Editor.prototype, "getIsReadonly", 1);
8678
8734
  __decorateClass([
8679
8735
  import_utils.bind
8680
- ], Editor.prototype, "_setShiftKeyTimeout", 1);
8736
+ ], Editor.prototype, "_releaseShiftKey", 1);
8681
8737
  __decorateClass([
8682
8738
  import_utils.bind
8683
- ], Editor.prototype, "_setAltKeyTimeout", 1);
8739
+ ], Editor.prototype, "_releaseAltKey", 1);
8684
8740
  __decorateClass([
8685
8741
  import_utils.bind
8686
- ], Editor.prototype, "_setCtrlKeyTimeout", 1);
8742
+ ], Editor.prototype, "_releaseCtrlKey", 1);
8687
8743
  __decorateClass([
8688
8744
  import_utils.bind
8689
- ], Editor.prototype, "_setMetaKeyTimeout", 1);
8745
+ ], Editor.prototype, "_releaseMetaKey", 1);
8690
8746
  function alertMaxShapes(editor, pageId = editor.getCurrentPageId()) {
8691
8747
  const name = editor.getPage(pageId).name;
8692
8748
  editor.emit("max-shapes", { name, pageId, count: editor.options.maxShapesPerPage });